網站首頁 編程語言 正文
緩存淘汰策略
標題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();
}
}
原文鏈接:https://blog.csdn.net/Miaoshuowen/article/details/125903181
相關推薦
- 2022-09-28 Python變量定義的簡單使用介紹_python
- 2022-08-04 Go?slice切片使用示例詳解_Golang
- 2022-04-23 uniapp文件上傳(任意文件,多文件上傳)
- 2022-12-24 Kotlin協程Flow生命周期及異常處理淺析_Android
- 2022-12-23 Android中Intent與Bundle的使用詳解_Android
- 2023-01-26 Android?源碼淺析RecyclerView?ItemAnimator_Android
- 2022-12-14 WPF實現XAML轉圖片的示例詳解_C#教程
- 2022-04-06 一篇文章帶你了解C/C++的回調函數_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支