赞
踩
课程内容:
在Redis中存放对象使用 json和二进制有哪些区别吗?
二进制只能在java语言中使用,json可以跨语言、跨平台,阅读性更强
RDB与AOF同步的区别:
RDB属于全量同步(定时同步)
优点:同步效率非常高
缺点:数据可能会丢失
AOF属于增量同步 有点偏向实时
优点:同步效率比较低,最多只会丢失1s中数据
平衡点:既然要效率高、数据不丢失使用 aof的everysec模式
如果aof与rdb都开启优先使用aof。
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。当数据达到限定大小后,会选择配置的策略淘汰数据。
Redis中的自动过期机制
实现需求:处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态
实现方案:
原理:
使用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失效监听
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);
}
}
测试结果:
Redis事务操作
Multi 开启事务
EXEC 提交事务
Discard 取消提交事务
注意:Redis官方是没有提供回滚方法,只提供了取消提交事务。
Redis中本身就是单线程的,能够保证线程安全问题。
思考:Redis中事务与Mysql中的事务有哪些区别?
mysql两个客户端对数据库同一行数据做更新操作,产生行锁,事务没有提交其他线程无法操作数据;multi只是开启事务,不能像mysql一样产生行锁,在redis中使用multi开启事务,其他的线程还是可以对该key执行set操作的。
Watch 可以监听一个或者多个key,在提交事务之前是否有发生了变化,如果发生了变化就不会提交事务,没有发生变化才可以提交事务。
原理:版本号 乐观锁
watch name
multi
set name zhangsan(此时另一个线程 set name lisi)
exec(提交zhangsan失败)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。