微服务架构-Redis实战原理剖析-074:Redis六大淘汰策略&事务原理&过期key事件监听

1 回顾上节课Redis核心内容

课程内容:

  1. Redis六大内存淘汰策略
  2. 基于RedisKey自动过期事件通知
  3. 基于RedisKey过期事件实现订单超时
  4. 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分钟未支付自动更改订单状态

实现方案:

  1. 采用定时任务,30分钟后检查该笔订单是否已经支付;
  2. 使用Redis Key自动过期机制触发事件通知

原理:

  1. 创建订单的时候绑定一个订单token 存放在redis中(有效期只有30分钟)
    key=token value 订单id
    Key= de293ead-7314-499d-a308-cd048c2879ce value 1574167533250
  2. 对该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失败)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值