redis lua


  需求是在緩存最近一周內用戶所有消息列表,考慮用Redis 存儲;為每個存儲一個獨立Sorted Set,value 為消息體,Score 為MessageId,用以實現增量消息同步。

  問題就來了:Sorted Set 怎么清理?

    -設計內存容量只允許放一周內最新的,太久了緩存意義不大,太浪費。

    -再者存在百萬級/s群發請求,不允許寫入時觸發清理。

  理想模型:如果使用磁盤則使用MyIsam堆表,數據按照順序寫入,再建立以uid為索引,刪除卻是完全順序的。內存里面的話Hash 表 + RB 樹兩個維度索引,RB樹可按照時間順序清理。

  解決方案:

    - 寫入第一條時,設置一周過期時間

      判斷是否第一條:zadd key 0 0 value score 返回2 說明第一條,1 不是第一條,只是多一條0的數據

    - 用戶每天第一次登陸,觸發一次清理

      清理需要遍歷Sorted Set上,消息一般不小,浪費io流量了,所以考慮采用lua 腳本實現。

    - 這樣保證,通過pipeline只是高並發寫入,同時保證活躍用戶一周內消息都在內存(不活躍不保證),清理簡單

  

清理腳本如下:

local ltime = 0
local dels = 0
local lefts = 0
local list = redis.call("ZRANGE", KEYS[1], 1, -1)
if(list[1] == nil) then
    return {-1, 0}
end
for _,v in ipairs(list) do
    if lefts == 0 then
        ltime = struct.unpack('<i', v)
        if ltime < tonumber(KEYS[3]) then
            dels = dels + 1
        else
            lefts = lefts + 1
        end
    else
        lefts = lefts + 1
    end
end
if lefts > tonumber(KEYS[2]) then
    dels = dels + (lefts - tonumber(KEYS[2]))
    lefts = tonumber(KEYS[2])
end
if lefts == 0 then
    ltime = 0
    redis.call("DEL", KEYS[1])
elseif dels > 0 then
    redis.call("ZREMRANGEBYRANK", KEYS[1], 1, dels)
end
return {dels, ltime}

  寫入的消息前4byte 為little-endian 的UnixTime,Redis lua 支持struct,很簡單解析出(當然也支持cjson,但速度要差一些).

  清理過期數據,並返回最后一條寫入的時間,應用根據返回時間適當延長過期時間。

  這里因為考慮每個人消息一般不會太多,所以全部遍歷,多的話可考慮分部分遍歷,如10條10條來,最新的就不會被不必要的取出來了,怎么說遍歷大Set還是較慢的。

   

clear_msgs(Uid, MaxLen, ExpireSec) ->
      ToExpires = utime() - ExpireSec,
      {ok, [Dels, LTime]} =
      eredis:q(pooler(Uid), [<<"EVAL">>, clear_script(), <<"3">>,
                              ?KEY_LIST(Uid), MaxLen, ToExpires]),
      {ok, binary_to_integer(Dels), binary_to_integer(LTime)}.

 

  性能:此段腳本在我機器上速度2.5w/s(列表長度10),  相比get 7w/s。速度很快,也節省網絡流量。

  

    script load

  此段腳本有700多字節,每次執行會帶來不少網絡流量;但對性能影響較小,內部對於eval 會先sha1 腳本,從緩存獲取生成好的lua 方法執行。

  當然最好使用script load,節省腳本傳輸、腳本的sha1計算,就行存儲過程一樣執行。

 

  luajit:

  github討論過 ,redis lua,相比nginx_lua 更像數據庫存儲過程,提供事務性的多個相關性操作,是否使用jit區別不大;

      支持的庫也很有限base、table、string、math、debug、cjson、struct、cmsgpack,能夠做的事情不多,也盡量別把太多邏輯用lua寫。

 

  redis.log 方法:

  調試大段的lua腳本,這個方法還是挺管用的。

 

  相關參考:

  官方說明:http://oldblog.antirez.com/post/scripting-branch-released.html

  源碼分析:http://blog.nosqlfan.com/html/4099.html

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM