微服务架构-Redis实战原理剖析-074:Redis六大淘汰策略&事务原理&过期key事件监听
作者:互联网
074:Redis六大淘汰策略&事务原理&过期key事件监听
1 回顾上节课Redis核心内容
课程内容:
- Redis六大内存淘汰策略
- 基于RedisKey自动过期事件通知
- 基于RedisKey过期事件实现订单超时
- Redis实现对key的事务操作
在Redis中存放对象使用 json和二进制有哪些区别吗?
二进制只能在java语言中使用,json可以跨语言、跨平台,阅读性更强
RDB与AOF同步的区别:
RDB属于全量同步(定时同步)
优点:同步效率非常高
缺点:数据可能会丢失
AOF属于增量同步 有点偏向实时
优点:同步效率比较低,最多只会丢失1s中数据
平衡点:既然要效率高、数据不丢失使用 aof的everysec模式
如果aof与rdb都开启优先使用aof。
2 Redis核心六大淘汰策略
Redis数据存放在内存里面,有可能会撑爆,防止内存撑爆使用淘汰策略
内存的淘汰策略:在Redis服务器上设置存放缓存的阈值(100mb 1g)
Redis用作缓存时,如果内存空间用满,就会自动驱逐老的数据。
Redis六种淘汰策略
noeviction:当内存使用达到阈值的时候,执行命令直接报错;
allkeys-lru:在所有的key中,优先移除最近未使用的key(推荐);
volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key;
allkeys-random:在所有的key中,随机移除某个key;
volatile-random:在设置了过期时间的键空间中,随机移除某个ke y;
volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除;
如何配置Redis淘汰策略
在redis.conf文件中,可以通过设置maxmemory 设置Redis内存大小的限制,比如:maxmemory 300mb。默认不开启,redis可用内存为当前服务器剩下的可使用内存。通过配置maxmemory-policy设置Redis的淘汰策略,比如:maxmemory-policy volatile-lru。当数据达到限定大小后,会选择配置的策略淘汰数据。
3 RedisKey过期回调监听
Redis中的自动过期机制
实现需求:处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态
实现方案:
- 采用定时任务,30分钟后检查该笔订单是否已经支付;
- 使用Redis Key自动过期机制触发事件通知
原理:
- 创建订单的时候绑定一个订单token 存放在redis中(有效期只有30分钟)
key=token value 订单id
Key= de293ead-7314-499d-a308-cd048c2879ce value 1574167533250 - 对该key绑定过期事件回调,执行回调方法传递key:e293ead-7314-499d-a308-cd048c2879ce,再查询数据库中对应订单状态是否支付,未支付说明订单超时库存+1。
4 Redis环境配置对key过期监听
使用Redis Key自动过期机制
当key失效时,可以执行客户端回调监听的方法,需要在Redis中配置:
notify-keyspace-events “Ex” (放开注释)
com.mayikt.listener
@Configuration
public class RedisListenerConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Autowired
private OrderMapper orderMapper;
/**
* 待支付
*/
private static final Integer ORDER_STAYPAY = 0;
/**
* 失效
*/
private static final Integer ORDER_INVALID = 2;
/**
* 使用该方法监听 当key失效的时候执行该方法
*
* @param message
* @param pattern
*/
@Override
public void onMessage(Message message, byte[] pattern) {
String expireKey = message.toString();
System.out.println("该key:expireKey:" + expireKey + "失效了");
// 加上前缀判断orderToken,满足条件的再查库;或者根据业务逻辑存放redis到不同库,根据库判断
OrderEntity orderNumber = orderMapper.getOrderNumber(expireKey);
if (orderNumber == null) {
return;
}
// 获取订单状态
Integer orderStatus = orderNumber.getOrderStatus();
// 如果该订单状态为待支付的情况下,直接将该订单修改为已经超时
if (orderStatus.equals(ORDER_STAYPAY)) {
orderMapper.updateOrderStatus(expireKey, ORDER_INVALID);
// 库存再加上1
}
}
}
测试redis key失效监听
5 基于Redis过期回调实现订单30分钟有效期
mysql执行
CREATE TABLE `order_number` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_name` varchar(255) DEFAULT NULL,
`order_status` int(11) DEFAULT NULL,
`order_token` varchar(255) DEFAULT NULL,
`order_id` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;
其他代码
@Data
public class OrderEntity {
private Long id;
private String orderName;
/**
* 0 待支付 1 已经支付
*/
private Integer orderStatus;
private String orderToken;
private String orderId;
public OrderEntity(Long id, String orderName, String orderId, String orderToken) {
this.id = id;
this.orderName = orderName;
this.orderId = orderId;
this.orderToken = orderToken;
}
}
@RestController
public class OrderController {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RedisUtils redisUtils;
@RequestMapping("/addOrder")
public String addOrder() {
// 1.提前生成订单token 临时且唯一
String orderToken = UUID.randomUUID().toString();
Long orderId = System.currentTimeMillis();
// 2.将token存放到redis中
redisUtils.setString(orderToken, orderId + "", 10L);
OrderEntity orderEntity = new OrderEntity(null, "蚂蚁课堂永久会员", orderId + "", orderToken);
return orderMapper.insertOrder(orderEntity) > 0 ? "success" : "fail";
}
}
public interface OrderMapper {
@Insert("insert into order_number values (null,#{orderName},0,#{orderToken},#{orderId})")
int insertOrder(OrderEntity OrderEntity);
@Select("SELECT ID AS ID ,order_name AS ORDERNAME ,order_status AS orderstatus,order_token as ordertoken,order_id as orderid FROM order_number\n" +
"where order_token=#{orderToken};")
OrderEntity getOrderNumber(String orderToken);
@Update("\n" +
"\n" +
"update order_number set order_status=#{orderStatus} where order_token=#{orderToken};")
int updateOrderStatus(String orderToken, Integer orderStatus);
}
@Component
public class RedisUtils {
/**
* 获取redis模版
*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void setString(String key, String value) {
setString(key, value, null);
}
public void setString(String key, String value, Long timeOut) {
stringRedisTemplate.opsForValue().set(key, value);
if (timeOut != null) {
stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
}
}
public String getString(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
}
@SpringBootApplication
@EnableCaching
@MapperScan("com.mayikt.mapper")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
测试结果:
6 Redis中的事务策略Multi
Redis事务操作
Multi 开启事务
EXEC 提交事务
Discard 取消提交事务
注意:Redis官方是没有提供回滚方法,只提供了取消提交事务。
Redis中本身就是单线程的,能够保证线程安全问题。
思考:Redis中事务与Mysql中的事务有哪些区别?
mysql两个客户端对数据库同一行数据做更新操作,产生行锁,事务没有提交其他线程无法操作数据;multi只是开启事务保证原子性,不能像mysql一样产生行锁,在redis中使用multi开启事务,其他的线程还是可以对该key执行set操作的。
7 Redis的Watch与Multi区别
Watch 可以监听一个或者多个key,在提交事务之前是否有发生了变化,如果发生了变化就不会提交事务,没有发生变化才可以提交事务。
原理:版本号 乐观锁
watch name
multi
set name zhangsan(此时另一个线程 set name lisi)
exec(提交zhangsan失败)
标签:074,String,过期,Redis,order,key,原理,public 来源: https://blog.csdn.net/u012425860/article/details/113899398