#LRU(Least recently used,最近最少使用)演算法根據資料的歷史存取記錄來進行淘汰數據,其核心思想是「如果數據最近被訪問過,那麼將來被訪問的幾率也更高」。
最常見的實作是使用一個鍊錶保存快取數據,詳細演算法實作如下:
新資料插入到鍊錶頭部;
每當快取命中(即快取資料被存取),則將資料移到鍊錶頭部;
當鍊錶滿的時候,將鍊錶尾部的資料丟棄。
在Java中可以使用LinkHashMap去實作LRU利用雜湊鍊錶實作:
在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(驅逐): 禁止驅逐資料
語法:
multiEXEC在一個交易中執行所有先前放入佇列的命令,然後恢復正常的連線狀態
語法:
execDISCARD清除所有先前在一個交易中放入佇列的命令,然後恢復正常的連線狀態。
語法:
discardWATCH當某個[交易需要按條件執行]時,就要使用這個指令將給定的[鍵設定為受監控]的狀態。
語法:
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 111Redis 不支援交易回滾(為什麼呢)大多數事務失敗是因為語法錯誤或類型錯誤,這兩種錯誤,在開發階段都是可以預見的Redis 為了效能方面就忽略了事務回滾。 Redis樂觀鎖樂觀鎖基於CAS(Compare And Swap)思想(比較並替換),是不具有互斥性,不會產生鎖等待而消耗資源,但是需要反复的重試,但也是因為重試的機制,能比較快速的反應。因此我們可以利用redis來
實現樂觀鎖定。具體想法如下:
然后去执行这个事务,如果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(); } }
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中文網其他相關文章!