基於Lua腳本解決實時數據處理流程中的關鍵問題

来源:http://www.cnblogs.com/zhu-wj/archive/2017/11/03/7777762.html
-Advertisement-
Play Games

在處理實時數據的過程中需要緩存的參與,由於在更新實時數據時併發處理的特點,因此在更新實時數據時經常產生新老數據相互覆蓋的情況,針對這個情況調查了Redis事務和Lua腳本後,發現Redis事務並不能很好的滿足該場景的業務需要,必須藉助Lua腳本執行原子化的操作才能在理論上解決數據更新的準確性問題。 ...


摘要

在處理實時數據的過程中需要緩存的參與,由於在更新實時數據時併發處理的特點,因此在更新實時數據時經常產生新老數據相互覆蓋的情況,針對這個情況調查了Redis事務和Lua腳本後,發現Redis事務並不能很好的滿足該場景的業務需要,必須藉助Lua腳本執行原子化的操作才能在理論上解決數據更新的準確性問題。

實時數據處理過程中遇到的問題

在處理實時數據的過程中,經常使用Redis存取數據執行CAS(check and set)操作。一般做法是先從Redis中獲取到目標數據,然後根據數據的特征指標判斷是應該更新還是放棄更新。在實時數據流量較小時這個辦法簡單粗暴的解決了數據更新的邏輯問題,但是面對上傳頻率較高的場景或者在更新實時數據時同步更新相關數據的彙總值時就會經常面臨更新時新數據被老數據覆蓋的問題,而且問題的出現具有隨機性,無法有效解決數據的緩存準確性的要求。

此過程中的調用示意圖:

        發送命令請求,獲取時間戳                    
Caller ----------------------------------------> Redis

                            發送時間戳給客戶端
Caller <---------------------------------------- Redis

        發送更新指令
Caller ----------------------------------------> Redis

                                    返回執行結果
Caller <---------------------------------------- Redis

由上圖可見在獲取時間戳到發送更新指令之前由於不是原子操作,因此存在數據被更新的可能。在解決這個問題的時候不禁會想:“如果這個場景發生在資料庫中會怎樣?”

如果在資料庫中,可以使用語句中的where條件來限制SQL語句的執行,做到按條件執行的目的。

上述思想可以用偽代碼表達為:

UPDATE 終端狀態 set status='XXXXX' where code='XXXX' and 時間戳>timestamp

這是個典型的先讀後寫的操作,該語句在資料庫中以鎖的方式保證了處理串列化和操作的原子性。

那麼,問題是:Redis事務中能不能做到?

Redis事務

Redis的事務由四個關鍵命令構成:MULTI、EXEC、DISCARD和WATCH構成。

名稱 作用
MULTI 聲明開啟事務通道
EXEC 開始批量執行
DISCARD 放棄執行之前發生的命令
WATCH 監聽某個KEY的變化

Redis事務的基本執行方式是:

  1. 使用WATCH聲明監聽某個KEY值的變化
  2. 發送MULTI命令
  3. 發送事務中需要執行的指令
  4. 發送EXEC指令,如果監聽到數據在watch後發生變化則放棄提交

Redis事務的執行示意圖:


        發送命令請求,獲取時間戳                    
Caller ----------------------------------------> Redis

                            發送時間戳給客戶端
Caller <---------------------------------------- Redis

        發送WATCH指令                  
Caller ----------------------------------------> Redis

        發送MULTI指令                  
Caller ----------------------------------------> Redis

        發送更新指令
Caller ----------------------------------------> Redis

        發送EXEC指令
Caller ----------------------------------------> Redis

                                    返回執行結果
Caller <---------------------------------------- Redis

Redis的事務跟資料庫的事務有極大不同,其事務實際是由WATCH監聽KEY值的變化加批量執行來完成的,而且事務執行過程中無法與客戶端進行交互的。這個事務的實現方式就限制了其所能滿足的業務場景,比如本文中遇到的時間戳+實時數據的更新場景中,要求時時刻刻都將最新的數據更新到Redis中,而不是在更新時發現有其他client搶先更新了目標KEY之後就放棄當前比較新的時間戳的更新權。

那麼,如何才能滿足將最新的數據更新到Redis中這個業務需求呢?方法也是有的,那就是使用Lua腳本

Redis+Lua腳本

Redis 2.6+都集成了Lua腳本。通過內嵌對於Lua的支持,Redis解決了長久以來不能高效處理CAS的缺點。在Redis中執行Lua腳本主要涉及到兩個關鍵命令:EVALEVALSHA,另外還有個輔助的命令SCRIPT EXISTS sha1 sha2 ... shaN可以用於查詢腳本是否已經緩存。

名稱 作用
EVAL 執行某個客戶端傳入的腳本
EVALSHA 執行某個已經在Redis Server中緩存的腳本

使用Lua腳本執行CAS操作的基本步驟:

  1. 客戶端發送EVAL 腳本 參數...命令
  2. 服務端執行腳本
    • 獲取用於進行判斷的鍵值
    • 判斷是否應該更新
    • 執行/放棄更新
  3. 返回腳本執行結果

其中第2步是完全在伺服器端執行,根據Redis官網的描述:

Redis uses the same Lua interpreter to run all the commands. Also Redis guarantees that a script is executed in an atomic way: no other script or Redis command will be executed while a script is being executed.

在Redis Server中執行Lua腳本是一個原子性的操作,時間戳較舊的數據會自動放棄更新緩存數據,因此就可以保證存入緩存中的數據永遠是最新的,因此也就解決了數據併發更新時老數據被新數據覆蓋的問題。

Lua腳本內部邏輯可以用偽代碼描述為:

timestamp=Redis.call('獲取時間戳的指令')

if (timestamp==nil)
then
    Redis.call('執行更新')
elseif (時間戳>timestamp)
then 
    Redis.call('執行更新')
end

使用Lua腳本的調用示意圖


          發送腳本
Caller ----------------------------------------> Redis

          為腳本創建 Lua 函數
Redis  ----------------------------------------> Lua

          綁定超時處理鉤子
Redis  ----------------------------------------> Lua

          執行腳本函數
Redis  ----------------------------------------> Lua

          返回函數執行結果(一個 Lua 值)
Redis  <---------------------------------------- Lua

          將 Lua 值轉換為 Redis 回覆
          並將結果返回給客戶端
Caller <---------------------------------------- Redis

總結

Redis中進行原子化操作有兩個方法:Redis事務或Lua腳本。使用Redis事務只能滿足數據在未發生變化進行更新而發生變化就放棄更新的場景。對於實時數據的處理場景來說,Redis的事務無法滿足根據時間戳進行業務處理的需要。由於Redis執行Lua腳本時是原子化的並且腳本內部可以編寫讀寫判斷邏輯,因此可以藉助Lua腳本完成實時數據更新的業務需要。

雖然使用Lua腳本可以較好的滿足業務需要,但是在使用Redis腳本時也有一定的註意事項,Lua腳本中不要編寫太複雜的操作,應該以儘量簡單的邏輯完成整個操作過程,避免因為腳本的執行產生阻塞效應。

參考資料

  1. Lua 腳本
  2. EVAL script numkeys key [key ...] arg [arg ...]
  3. Redis Transactions

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 實現效果: 實現原理: 給關閉按鈕綁定點擊事件,點擊以後觸發動畫效果。利用jQuery的animate方法,先讓顯示天氣的盒子高度變為0,接著讓整個包含天氣和事件的盒子寬度變為0,最後通過將display屬性值設為none,使得close按鈕消失。 實現代碼: ...
  • 一、函數聲明、函數表達式、匿名函數1.函數聲明:function fnName () {…};使用function關鍵字聲明一個函數,再指定一個函數名,叫函數聲明。2.函數表達式 var fnName = function () {…};使用function關鍵字聲明一個函數,但未給函數命名,最後將 ...
  • 最近在搞一個被很多人改了的框架,天天看代碼看的頭的暈了,不過感覺進步還挺大的,自己做了一個後臺可配置前臺查看兩個庫不同數據範圍的東西,還挺滿意,那天拿出來分享一下,今天先說一個這幾天做的功能,就是html頁面的查找功能。 這個功能主要是實現在查找框內輸入字元,之後按後面的上一個下一個按鈕,會自動把查 ...
  • 簡介 onunload,onbeforeunload都是在刷新或關閉時調用,可以在<script>腳本中通過 window.onunload來調用。區別在於onbeforeunload在onunload之前執行,它還可 以阻止onunload的執行。 onbeforeunload 是正要去伺服器讀 ...
  • 項目中需要調用webservice介面,android SDK中並沒有直接訪問webservice介面的方法,於是我引入了ksoap-android的jar包,來實現訪問webservice介面。剛開始一切還都比較順利,成功從webservice介面獲取到了返回的數據,直接運行在手機上一切也都正常。 ...
  • 註意Vietnamese_CI_AS排序規則下的特殊字元大小敏感問題 最近,在SQL Server中遇到了Vietnamese_CI_AS排序規則的特殊字元的大小寫敏感問題,是的,你沒有看錯,這句話並沒有語病(DBA老司機懂的)。遇到這個特殊情況的時候,我也大跌眼鏡,顛覆我的一些常識,OK,閑話少說... ...
  • http://kingxss.iteye.com/blog/1741076 ...
  • 這個系列大致想跟大家分享以下篇章(我會持續更新的↖(^ω^)↗): 1、mongo 3.4分片集群系列之一:淺談分片集群 2、mongo 3.4分片集群系列之二:搭建分片集群--哈希分片 3、mongo 3.4分片集群系列之三:搭建分片集群--哈希分片 + 安全 4、mongo 3.4分片集群系列之 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...