在Redis中,如何运用缓存淘汰策略和事务实现乐观锁?
来源:亿速云
时间:2023-05-10 14:28:45 109浏览 收藏
数据库小白一枚,正在不断学习积累知识,现将学习到的知识记录一下,也是将我的所得分享给大家!而今天这篇文章《在Redis中,如何运用缓存淘汰策略和事务实现乐观锁?》带大家来了解一下##content_title##,希望对大家的知识积累有所帮助,从而弥补自己的不足,助力实战开发!
缓存淘汰策略
标题LRU原理
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:
新数据插入到链表头部;
每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
当链表满的时候,将链表尾部的数据丢弃。
在Java中可以使用LinkHashMap去实现LRU利用哈希链表实现:
标题Redis缓存淘汰策略
设置最大缓存
在 redis 中,允许用户设置最大使用内存大小maxmemory,默认为0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一定要设置。
redis 内存数据集大小上升到一定大小的时候,就会实行数据淘汰策略。
淘汰策略
redis淘汰策略配置:maxmemory-policy voltile-lru,支持热配置
redis 提供 6种数据淘汰策略:
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据
Redis事务
Redis事务介绍
Redis 的事务是通过 MULTI 、 EXEC 、 DISCARD 和 WATCH 、UNWATCH这五个命令来完成的。
Redis 的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。
Redis 将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
Redis 不支持回滚操作。 事务命令
MULTI
用于标记事务块的开始。Redis会逐个将后续命令放入队列中,随后使用EXEC命令以原子化方式执行该命令序列。
语法:
multi
EXEC
在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态
语法:
exec
DISCARD
清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。
语法:
discard
WATCH
当某个[事务需要按条件执行]时,就要使用这个命令将给定的[键设置为受监控]的状态。
语法:
watch key [key…]
注意事项:使用该命令可以实现 Redis 的乐观锁。
UNWATCH
清除所有先前为一个事务监控的键
语法:
unwatch
命令图解:
事务演示:
127.0.0.1:6379> multi OK 127.0.0.1:6379> set s1 111 QUEUED 127.0.0.1:6379> hset set1 name zhangsan QUEUED 127.0.0.1:6379> exec 1) OK 2) (integer) 1 127.0.0.1:6379> multi OK 127.0.0.1:6379> set s2 222 QUEUED 127.0.0.1:6379> hset set2 age 20 QUEUED 127.0.0.1:6379> discard OK 127.0.0.1:6379> exec (error) ERR EXEC without MULTI 127.0.0.1:6379> watch s1 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set s1 555 QUEUED 127.0.0.1:6379> exec # 此时在没有exec之前,通过另一个命令窗口对监控的s1字段进行修改 (nil) 127.0.0.1:6379> get s1 111
Redis 不支持事务回滚(为什么呢)
大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的Redis 为了性能方面就忽略了事务回滚。
Redis乐观锁
乐观锁基于CAS(Compare And Swap)思想(比较并替换),是不具有互斥性,不会产生锁等待而消耗资源,但是需要反复的重试,但也是因为重试的机制,能比较快的响应。因此我们可以利用redis来
实现乐观锁。具体思路如下:
利用redis的watch功能,监控这个redisKey的状态值
获取redisKey的值
创建redis事务
给这个key的值+1
然后去执行这个事务,如果key的值被修改过则回滚,key不加1
public void watch() { try { String watchKeys = "watchKeys"; //初始值 value=1 jedis.set(watchKeys, 1); //监听key为watchKeys的值 jedis.watch(watchkeys); //开启事务 Transaction tx = jedis.multi(); //watchKeys自增加一 tx.incr(watchKeys); //执行事务,如果其他线程对watchKeys中的value进行修改,则该事务将不会执行 //通过redis事务以及watch命令实现乐观锁 List<Object> exec = tx.exec(); if (exec == null) { System.out.println("事务未执行"); } else { System.out.println("事务成功执行,watchKeys的value成功修改"); } } catch (Exception e) { e.printStackTrace(); } finally { jedis.close(); } }
Redis乐观锁实现秒杀
public class RedisLock { public static void main(String[] arg) { //库存key String redisKey = "stock"; ExecutorService executorService = Executors.newFixedThreadPool(20); try { Jedis jedis = new RedisProperties.Jedis("127.0.0.1", 6378); // 可以被秒杀的库存的初始值,库存总共20个 jedis.set(redisKey, "0"); jedis.close(); } catch (Exception e) { e.printStackTrace(); } for (int i = 0; i < 1000; i++) { executorService.execute(() -> { Jedis jedis1 = new Jedis("127.0.0.1", 6378); try { jedis1.watch(redisKey); String redisValue = jedis1.get(redisKey); int valInteger = Integer.valueOf(redisValue); String userInfo = UUID.randomUUID().toString(); // 没有秒完 if (valInteger < 20) { Transaction tx = jedis1.multi(); tx.incr(redisKey); List list = tx.exec(); // 秒成功 失败返回空list而不是空 if (list != null && list.size() > 0) { System.out.println("用户:" + userInfo + ",秒杀成 功!当前成功人数:" + (valInteger + 1)); } // 版本变化,被别人抢了。 else { System.out.println("用户:" + userInfo + ",秒杀失 败"); } } // 秒完了 else { System.out.println("已经有20人秒杀成功,秒杀结束"); } } catch (Exception e) { e.printStackTrace(); } finally { jedis1.close(); } }); } executorService.shutdown(); } }
文中关于redis的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《在Redis中,如何运用缓存淘汰策略和事务实现乐观锁?》文章吧,也可关注golang学习网公众号了解相关技术文章。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
342 收藏
-
361 收藏
-
159 收藏
-
164 收藏
-
221 收藏
-
156 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习