怎么使用Redis缓存淘汰策略和事务实现乐观锁
缓存淘汰策略
标题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缓存淘汰策略和事务实现乐观锁的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

Redis集群模式通过分片将Redis实例部署到多个服务器,提高可扩展性和可用性。搭建步骤如下:创建奇数个Redis实例,端口不同;创建3个sentinel实例,监控Redis实例并进行故障转移;配置sentinel配置文件,添加监控Redis实例信息和故障转移设置;配置Redis实例配置文件,启用集群模式并指定集群信息文件路径;创建nodes.conf文件,包含各Redis实例的信息;启动集群,执行create命令创建集群并指定副本数量;登录集群执行CLUSTER INFO命令验证集群状态;使

如何清空 Redis 数据:使用 FLUSHALL 命令清除所有键值。使用 FLUSHDB 命令清除当前选定数据库的键值。使用 SELECT 切换数据库,再使用 FLUSHDB 清除多个数据库。使用 DEL 命令删除特定键。使用 redis-cli 工具清空数据。

使用 Redis 指令需要以下步骤:打开 Redis 客户端。输入指令(动词 键 值)。提供所需参数(因指令而异)。按 Enter 执行指令。Redis 返回响应,指示操作结果(通常为 OK 或 -ERR)。

使用Redis进行锁操作需要通过SETNX命令获取锁,然后使用EXPIRE命令设置过期时间。具体步骤为:(1) 使用SETNX命令尝试设置一个键值对;(2) 使用EXPIRE命令为锁设置过期时间;(3) 当不再需要锁时,使用DEL命令删除该锁。

要从 Redis 读取队列,需要获取队列名称、使用 LPOP 命令读取元素,并处理空队列。具体步骤如下:获取队列名称:以 "queue:" 前缀命名,如 "queue:my-queue"。使用 LPOP 命令:从队列头部弹出元素并返回其值,如 LPOP queue:my-queue。处理空队列:如果队列为空,LPOP 返回 nil,可先检查队列是否存在再读取元素。

Redis 使用哈希表存储数据,支持字符串、列表、哈希表、集合和有序集合等数据结构。Redis 通过快照 (RDB) 和追加只写 (AOF) 机制持久化数据。Redis 使用主从复制来提高数据可用性。Redis 使用单线程事件循环处理连接和命令,保证数据原子性和一致性。Redis 为键设置过期时间,并使用 lazy 删除机制删除过期键。

理解 Redis 源码的最佳方法是逐步进行:熟悉 Redis 基础知识。选择一个特定的模块或功能作为起点。从模块或功能的入口点开始,逐行查看代码。通过函数调用链查看代码。熟悉 Redis 使用的底层数据结构。识别 Redis 使用的算法。

Redis 作为消息中间件,支持生产-消费模型,可持久化消息并保证可靠交付。使用 Redis 作为消息中间件可实现低延迟、可靠和可扩展的消息传递。
