Redis的线程模型和事务( 四 )
持久性(Durability)
持久性是指一个事务一旦被提交了 , 那么对数据库中的数据的改变就是永久性的 , 即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作 。
Redis 是否具备持久化 , 这个取决于 Redis 的持久化模式:
- 纯内存运行 , 不具备持久化 , 服务一旦停机 , 所有数据将丢失 。
- RDB 模式 , 取决于 RDB 策略 , 只有在满足策略才会执行 Bgsave , 异步执行并不能保证 Redis 具备持久化 。
- AOF 模式 , 只有将 appendfsync 设置为 always , 程序才会在执行命令同步保存到磁盘 , 这个模式下 , Redis 具备持久化 。 (将 appendfsync 设置为 always , 只是在理论上持久化可行 , 但一般不会这么操作)
- Redis 具备了一定的原子性 , 但不支持回滚 。
- Redis 不具备 ACID 中一致性的概念 。 (或者说 Redis 在设计时就无视这点)
- Redis 具备隔离性 。
- Redis 通过一定策略可以保证持久性 。
4.3. 代码这里结合springboot代码做示例 , 加深我们对Redis事务的应用开发 。 在springboot中构建Redis客户端 , 一般通过 spring-boot-starter-data-redis 来实现 。
jedis 和 lettuce
Lettuce和Jedis的都是连接Redis Server的客户端程序 。 Jedis在实现上是直连redis server , 多线程环境下非线程安全 , 除非使用连接池 , 为每个Jedis实例增加物理连接 。 Lettuce基于Netty的连接实例(StatefulRedisConnection) , 可以在多个线程间并发访问 , 且线程安全 , 满足多线程环境下的并发访问 , 同时它是可伸缩的设计 , 一个连接实例不够的情况也可以按需增加连接实例 。
可见Lettuce是要优于Jedis的 , 在 spring-boot-starter-data-redis 早期版本都是使用Jedis连接的 , 但到了2.x版本 , Jedis就直接被替换成Lettuce 。
下面直接看代码吧 。
pom
pom文件主要是引入了 spring-boot-starter-data-redis。
org.springframework.boot spring-boot-starter-data-redis
controllercontroller中定义了两个接口:
- 接口1 watch:watch键A , 在事务中修改键A和B的值 , 在阻塞3秒后 , 提交事务 。
- 接口2 change:修改键A 。
@RestControllerpublic class DemoController {public final static String STR_KEY_A="key_a";public final static String STR_KEY_B="key_b";private final StringRedisTemplate stringRedisTemplate;public DemoController(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@GetMapping("/watch")public void watch(){stringRedisTemplate.setEnableTransactionSupport(true);stringRedisTemplate.watch(STR_KEY_A);stringRedisTemplate.multi();try {stringRedisTemplate.opsForValue().set(STR_KEY_A, "watch_a");stringRedisTemplate.opsForValue().set(STR_KEY_B, "watch_b");Thread.sleep(3000);}catch (Exception e){e.printStackTrace();stringRedisTemplate.discard();}stringRedisTemplate.exec();stringRedisTemplate.unwatch();}@GetMapping("/change")public void change(){stringRedisTemplate.opsForValue().set(STR_KEY_A,"change_a");}}
测试用例我们写一个测试用例 , 大致逻辑是:先调用接口1 , 0.5秒后(为了保证接口1先于接口2执行 , 因为线程实际执行顺序不一定按照业务代码顺序来) , 再调用接口2 , 并且在两个接口的线程中 , 都会将键A和B的值打印出来 。
推荐阅读
- Google AI建立了一个能够分析烘焙食谱的机器学习模型
- OpenAI推DALL-E模型:能根据文字描述生成图片
- Intel 12代CPU曝光:不只16核24线程
- AMD Zen3线程撕裂者曝料:16核心回归、名字凌乱
- 英特尔Alder Lake-S早期工程样品曝光:16核/24线程 主频仅1.4GHz
- AMD确认Zen3架构新一代锐龙线程撕裂者处理器:要明年见了
- 谷歌搜索的灵魂!BERT模型的崛起与荣耀
- java 从零实现属于你的 redis 分布式锁
- Redis集群做法的难点,百万并发客户端「实战」
- 如何优雅的停止一个线程?