Redis深度歷險記(三)鎖&事務

先來官網說明:

The SET command supports a set of options that modify its behavior:

  • EX seconds – Set the specified expire time, in seconds.
  • PX milliseconds – Set the specified expire time, in milliseconds.
  • NX – Only set the key if it does not already exist.
  • XX – Only set the key if it already exist.
  • KEEPTTL – Retain the time to live associated with the key.

Note: Since the SET command options can replace SETNX, SETEX, PSETEX, it is possible that in future versions of Redis these three commands will be deprecated and finally removed.

按照官網說明,哪些setnx,setex,psetex後面都不要用了

獲取

一般是先獲取鎖,然後根據結果來決定是否改修改時間,現在這些可以一條指令完成,無需事務

set lock:Qbit true ex 5 nx

釋放

上面的加鎖只是保證了當加鎖者掛了之後鎖會被自動釋放,但問題是如果加鎖者沒有掛而是執行太久導致鎖被釋放,然後這個鎖被第二個線程獲取,那麼鎖會被第一個加鎖者釋放麼?如果第一個加鎖者使用DEL那麼結果是會被釋放的,爲了解決這個問題,需要給鎖加個標識(隨機數),這樣大家就只釋放自己的鎖,同時也可以感知到自己的超時,從而報錯.書中給出的方案如下(lua)

tag =random.nextint()
if redis.set(key,tag,nx=True,ex=5):
	do_something()
	redis.delifequals(key,tag)
# delifequals
if redis.call("get",KEYS[1])==ARGV[1] then
	return redis.call("del",KEYS[1])
else
	return 0
end

spring的方案沒有考慮這個問題,這裏附上老版spring的解決方案

local lockClientId = redis.call('GET', KEYS[1])
if lockClientId == ARGV[1] then
  redis.call('PEXPIRE', KEYS[1], ARGV[2])
  return true
elseif not lockClientId then
  redis.call('SET', KEYS[1], ARGV[1], 'PX', ARGV[2])
  return true
end
return false

上面的lockClientId是一個uuid需要記下來.
新版的spring採用了set ex nx的語法,關鍵代碼如下

RedisSerializer<String> serializer = RedisLockRegistry.this.redisTemplate.getStringSerializer();
byte[][] actualArgs = new byte[][] {
	serializer.serialize(constructLockKey()),
	RedisLockRegistry.this.lockSerializer.serialize(RedisLock.this),
	serializer.serialize("NX"),
	serializer.serialize("EX"),
	serializer.serialize(String.valueOf(expireAfter))
};
return connection.execute("SET", actualArgs) != null;

注意這裏nx在ex前面,也是可以的

expire

這個命令也許用的不少,但是官網上還是提到不少騷操作.

系統時鐘

按照官網介紹Redis是跟隨系統時間來處理的,所以系統時間的更改就相當於時間的流逝.所以官網支持當遷移Redis的存儲文件時,如果兩個系統的時鐘不一致,會導致一些key的失效,同樣,如果修改了系統時間也會導致key失效,下面是原文說明:

Even running instances will always check the computer clock, so for instance if you set a key with a time to live of 1000 seconds, and then set your computer time 2000 seconds in the future, the key will be expired immediately, instead of lasting for 1000 seconds.

各種key操作

按照官方文檔,很多對key的操作(但不是所有)是不會引起過期時間的改變,所以它不像Web開發中的Session那樣可以自動延期.具體這些那些指令,我建議用到的時候再去翻官網

集羣

首先,由於Master和Replica都有過期時間,所以即使Replica沒有等到Master的DEL指令,也不會返回過期的Key,其次這樣保證了Master掛掉後,新的Master也能繼續DEL過期的key

分佈鎖

理論上,一個client在master加鎖成功,然後master還沒來得及告訴replica的時候自己就掛了,然後replica稱爲新的master後又會接受加鎖,這樣就會出現問題,於是出現了分佈式鎖
其簡單原理是向無關聯的redis發送**set(key,value,nx=true,ex=xxx)指令,大多數成功就認爲加鎖成功,當釋放時需要向所有節點刪除.這樣大量的io必然帶來性能下降

過期策略

passive & active

大多數人都知道有passiveactive兩種策略,其中passive確保了不會讀到過期數據,而active則爲了節省空間,官方提供了一個trivial probabilistic algorithm,其過程如下

Specifically this is what Redis does 10 times per second:

  1. Test 20 random keys from the set of keys with an associated expire.
  2. Delete all the keys found expired.
  3. If more than 25% of keys were expired, start again from step 1.

可以看出來,這種算法在垃圾回收消耗和內存使用中做出了一種平衡,並且做了一個統計上的假設.
另外主節點纔會執行active策略,並將DEL指令寫入AOF從而讓從節點執行

驚羣

對於批量設置了大量的key,如果僅僅考慮自動回收,難麼把超時時間設置爲不一樣的隨機數,避免同時過期.

maxmemory-policy

  • noviction
    不再允許新增數據(可以刪除),否則報錯
  • volatile-lru
  • volatile-ttl
  • volatile-random
  • allkeys-lru
  • allkeys-random

書中說緩存用allkeys-xxx策略,不敢苟同

LRU

這裏說下Redis的Approx LRU,它是給所有key加了個最後一次被訪問的時間戳,當觸發被動回收(內存超了)就隨機找幾個(默認是5),幹掉一個,看下內存超了沒,超了就繼續.到了Redis3.0有了一個淘汰池,就是把候選人和淘汰池裏的再比較一次,有了一次獲取緩刑的機會.

unlink

這是一個優化的回收的機制,內部會根據key大小來決定是否異步處理

事務

原子性

事務在遇到指令執行失敗後,後面的指令還會繼續執行…Redis的事務根本不具備原子性,而僅僅是滿足了事務隔離性中的串行化–當前事務有着不被其他事務打斷的權利

pipeline

管道的本質…客戶端通過改變了讀寫的順序帶來的性能的巨大提升

所以一般事務會結合pipeline一起使用

watch

watch是用在事務開啓後,提交前防止數據被別人修改導致不一致,所以一般順序是:

  1. watch變量
  2. 讀取變量
  3. multi 開啓事務
  4. 將變量寫回
  5. exec提交事務:此處可能失敗,則重新回到第1步

一旦multi後就不能watch了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章