日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis 集群分布式锁与 API 网关分布式限流

發布時間:2025/4/5 数据库 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis 集群分布式锁与 API 网关分布式限流 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

https://www.infoq.cn/article/FoQGIk*BzdQWJJ0tKqrJ

Redis 集群的歷史

Redis 在 3.0 前一般有兩種集群方案,一是 proxy(Twemproxy、Codis),二是使用 Sentinel(哨兵)。 通過 Sentinel 是一種使用哨兵來達到高可用的方案,而 proxy 是用于在前置上進行 sharding 用代理給后端的 redis 集群的方案,達到負載均衡的方案,在單個分片的 redis 中作主從。 因為本文要重點講解的不是 3.0 前的方案,因此說的比較粗略。

Redis3.0 提供了官方的 Redis cluster 機制支持。主要通過內部無中心的多個節點來達到集群、高可用的作用。下面是 Redis Cluster 的架構圖:

Redis Cluster 的重要機制

  • sharding 由 Redis cluster 根據 client 調用 redis 的 key 進行 hash 取模得到一個 code,根據這個 code 放到 16384 個 slot 中。在以上的架構圖中 slot1 組、slot2 組、slot3 組服務器中分別是對應的差不多 1/3 的 slot。這樣就得到了根據 key 的 shading。

  • 說到 sharding 就會說到怎么進行擴容和縮容,redis cluster 也提供了工具進行遷移。 不過由于不是一致性 hash,所以涉及到遷移數據的節點數會多于一致性 hash,但是遷移的量還是可控的,只會遷移部分以達到平均的效果。 (redis cluster 遷移詳細機制需要另外詳細研究)

  • 在 redis cluster 的一個 slot 組中,采用的是主備的高可用模式,只有 master 對外提供服務,如果 master 掛掉,則 slave 會成為新的 master,由新的 master 提供服務,在切換的過程中會在短時間的 redis 服務不可用。

  • redis cluster 進入 fail 狀態的條件:

    • 如果同時有大于 N(N 為總 slot 組數)*1/2 的 master 節點數同時掛掉,則 cluster 進入 fail 狀態,因為剩下的 master 已經無法選出新的 master 節點了。 分布式系統中必須要有大于 N(N 為總 slot 組數)*1/2 的節點存在才能判斷有效。 節如果有是 3 主 3 備的集群,如果同時有 2 個 master 節點掛掉了,那么集群就掛掉了。
    • 如果一個 slot 組中的服務器已經沒有備用服務器了,這時 master 掛掉后,redis cluster 也會進入 fail 狀態。 因為部分 slot 的數據已經查詢不到了。
  • redis cluster 具備高可用、sharding、負載均衡的功能。

  • 如果多個 key 想要人為控制落到一個 slot 組上,可以通過對 key 進行改造實施。即如果 key 都以{key_pre}idxxxx 這樣,那么所有的 key 將是以 key_pre 去確定 slot 組,這樣就達到了以{key_pre}開頭的 key 都會是在一個 slot 組。

  • 當向一個 master 中寫入數據時,數據是進行迅速返回的,返回后再進行主從同步的方式向 slave 進行同步,因此這里是損失了 CAP 中的一致性的。 在將來的 redis 版本中可能會開放同步寫的方式寫入 slave,以維護一致性,當然這樣會損失一定的寫入速度。

  • 在上文中提到的在做主從切換的時候,會有短時的不可用狀態,因此會操作分布式理論 CAP 中可用性。

  • 單個 redis 服務器上的請求是順序執行的,因為 redis 服務器是單進程、單線程的。

  • 分布式鎖

    分布式鎖有很多的實現方案,通常有數據庫、文件系統、zookeeper、redis。下面講述基于 redis cluster 的分布式鎖方案。

    基于 redis 事務實現

    嚴格來說這并不是分布式鎖,只是通過改造可以實現鎖的效果。這里并不是實現鎖定其他的線程被阻塞的效果,而是如果數據被其他客戶端修改了就返回失敗。原理是基于 reids 的 multi 和 watch 命令。 在事務開始前對要鎖到的數據進行 watch,進行業務操作后,如果發現鎖定的數據已經變了,就提交失敗,重新進行業務操作。 在這個方案中如果執行失敗就一直反復執行直到成功,也是實現了多個 redis 客戶同時修改一個數據時的協調的鎖的功能。

    偽代碼如下:

    復制代碼
    ? jedis.set("balance", String.valueOf(balance));
    ??
    ? jedis.watch("balance");
    ??
    ? Transaction transaction = jedis.multi();
    ??
    ? transaction.decrBy("balance",amtToSubtract);/
    ??
    ? transaction.incrBy("debt", amtToSubtract);
    ??
    ? List result = transaction.exec();// 執行事務
    ??
    ? if(result == null){
    ??
    ? // 重新執行事務或者其他。
    ??
    ? }else{
    ??
    ? // 事務執行成功。
    ??
    ? }

    基于 setNX EX 實現

    Redis set key 時的一個 NX 參數可以保證在這個 key 不存在的情況下寫入成功。并且再加上 EX 參數可以讓該 key 在超時之后自動刪除。

    jedis 偽代碼:

    復制代碼
    ? String set(String key, String value, String nxxx, String expx,longtime);

    為什么要以一代代碼一個命令去實現呢,是因為要防止 NX 后突然就宕機了會產生死鎖。

    過期的時間的設置上要考慮幾個問題:

  • 如果時間設置比較長,如果鎖定發起 server 發生宕機,那么很久都解鎖不了。

  • 如果時間設置比較短,可能會發生業務還沒有做完,發生了解鎖,沒有起到鎖定的作用。

  • 關于解鎖:在解鎖時需要判斷當前的鎖是不是自己所鎖定的,因此需要在加鎖時將 key 的 value 設置為一個隨機數,在解鎖時進行判斷。 因為在解鎖時有 get 數據再判斷的多個操作,因此這里也需要防止并發問題,因此使用 lua 腳本寫這個操作。如以下偽代碼:

    復制代碼
    ? $script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    ??
    ? $result = $this->redis->eval(script,array($key,$val),1);

    注意一個可能發生的問題:

    redis 的主從異步復制機制可能丟失數據,會出現如下場景:A 線程獲得了鎖,但鎖數據還未同步到 slave 上,master 掛了,slave 頂成主,線程 B 嘗試加鎖,仍然能夠成功,造成 A、B 兩個線程并發訪問同一個資源。

    基于 lua 腳本實現

    由于 redis 服務器的單進程單線程模型,因此產生這種被大量使用的分布式鎖的方案。

    lua 腳本通過 eval 或者 evalsha 方法進行執行,同時將腳本涉及到的 key 和參數傳遞給 redis 服務器執行。客戶端可以通過 jedis 進行調用。 evalsha 是對腳本在 redis 服務器進行預編譯,這樣可以減少網絡交互量,加速執行時的速度。

    注意以下:

  • 由于是分布式集群環境,如果傳遞了多個 key,而 key 處于不同的 slot 組服務器,那么執行將會報錯。

  • lua 腳本中由于是單進程單線程執行,因此不要做消耗時間的操作。 在簡單操作的情況下,在 CPU 6 核 Intel? Core? i7-2720QM CPU @ 2.20GHz 內存 16GB 的情況下能跑出 5 萬 TPS。

  • 如果要進行 TPS 擴容,則需要通過 key 對應的 slot 組不同,將 lua 分發到不同的 slot 組中的 redis master 服務器去執行。

  • 程序都建議使用 evalsha 的方法去執行,這樣可以提高 TPS。

  • API 網關分布式限流

    API 網關中針對一個 API、API 分組、接入應用 APP ID,IP 等進行限流。這些限流條件都將會產生一個限流使用的 key,在后續的限流中都是對這個 key 進行限流。

    限流算法通常在 API 網關中可以采用令牌桶算法實現。

    必須說明一點的是分布式限流由于有網絡的開銷,TPS 的支持隔本地限流是有差距的,因此在對于 TPS 要求很高的場景,建議采用本地限流進行處理。

    下面討論我們應該采用 redis 的哪一種分布式鎖的方案:

    由于 redis 事務要得到鎖的效果需要在高 TPS 時會產生大量的無效的訪問請求,所以不建議在這種場景下使用。

    SET NX/EX 的鎖方案會產生在過期時間的問題,同時也有異步復制 master 數據到 slave 的問題。相比 lua 方案會產生更多的不穩定性。

    我建議采用 lua 的方案來實施分布式鎖,因為都是單進程單線程的執行,因此在 TPS 上和第二種方案沒有大的區別,而且由于只是一個 lua 腳本在執行,甚至是可能純 lua 執行可能會有更高的 TPS。 當然是 lua 腳本中可能還是會去設置過期時間,但是應用 server 宕機并不會影響到 redis 中的鎖。 當然 master 異步復制的問題還是有, 但是并不會造成問題,因為數據只會有 1 個 lua 腳本執行問題,下一個執行就正常了。

    在實現方案的時候使用了 Jedis 庫,有一些問題在方案的實現層面我已經去做過驗證了,可能也會是讀者的疑問。

  • 理論上 jedis 都應該是連接 redis 集群中的 master 節點,因為 redis 主備是采用的主備(Active-Standby 方式)的方式。 需要確定在這種情況下應該是配置所有的 redis 節點還是只配置 master。 理論上是需要配置所有的節點。
  • 答:配置所有節點,做自動轉發。

  • 需要測試在其中一個 slot 組的 master 掛掉后,slave 成為 master 后,jedis cluster 的方式還能正常的訪問對應的 key。從資料上來看是能自動處理的。
  • 答:能自動處理。

  • 因為是采用 redis cluster,所以需要測試 key 存儲在 slot 1 組上時,jedis cluster 連接的是 slot 2 組上的 redis 服務器,在這種情況下要能正常的調用 lua。 如果不能要看怎么樣調用,因為 redis 可能會返回 "MOVED" 指令,這個要看 jedis 組件有沒有實現自動的遷移。
  • 答:能自動處理。

  • 需要測試連接的是 salve 節點,但是讀請求是轉給了 master 節點,salve 節點不處理請求。因為是主備(Active-Standby 方式)狀態。
  • 答:能自動處理,由 Jedis jar 進行維護節點的狀態,查詢最新的 master 節點的信息。

    轉載于:https://www.cnblogs.com/davidwang456/articles/10191761.html

    總結

    以上是生活随笔為你收集整理的Redis 集群分布式锁与 API 网关分布式限流的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。