使用PostgreSQL替代Redis實現佇列、分散式鎖和釋出/訂閱

banq發表於2021-06-13

兩種常用架構
  • 用於資料儲存的 PostgreSQL
  • Redis 用於協調後臺作業佇列(以及一些有限的原子操作)

Redis 非常棒,但如果我告訴你這個堆疊最常見的用例實際上可以只使用 PostgreSQL 來實現呢?很有可能您使用 Redis 做的事情實際上也適用於 PostgreSQL。跳過Redis並節省依賴多個資料服務的運營成本和開發複雜性可能是一個值得的權衡。(banq注:擴充套件性從小到大:本文方式<Redis<kafka,複雜性是倒過來的)
 

用例 1:作業佇列
也許我見過的 Redis 最常見的用途是協調從您的 Web 服務到後臺工作人員池的作業分派。這個概念是,您希望記錄執行某些後臺工作的願望(可能帶有一些輸入資料),並確保您的眾多後臺工作人員中只有一個會監聽它。Redis 對此有所幫助,因為它為其資料結構提供了一組豐富的原子操作。
但是自從 9.5 版引入後,PostgreSQL 就有了語句的SKIP LOCKED選項SELECT ... FOR ...(這裡是文件)。當指定這個選項時,PostgreSQL 將忽略任何需要等待被釋放鎖的行。

BEGIN;

WITH job AS (
  SELECT
    id
  FROM
    jobs
  WHERE
    status = 'pending'
  LIMIT 1
  FOR UPDATE SKIP LOCKED
)
UPDATE
  jobs
SET
  status = 'running'
WHERE
  jobs.id = job.id
RETURNING
  jobs.*;

COMMIT;

透過指定FOR UPDATE SKIP LOCKED,將為從 返回的任何行隱式獲取行級鎖SELECT。此外,因為您指定了SKIP LOCKED,此語句不可能阻塞另一個事務。如果有另一個作業準備處理,它將被返回。由於行級鎖定,執行此命令的多個工作人員無需擔心接收同一行。
這種技術的最大警告是,如果您有大量工作人員試圖退出此佇列,並且有大量工作為他們提供服務,他們可能會花一些時間單步執行工作並嘗試獲取鎖。在實踐中,我開發的大多數應用程式的後臺工作人員都不到十幾個,而且成本可能不會很高。
 

用例 2:應用程式鎖
假設您有一個與第三方服務的同步例程,並且您只想為所有伺服器程式中的任何給定使用者執行它的一個例項。這是我見過的 Redis 的另一個常見應用:分散式鎖定。
PostgreSQL 也可以使用它的Advisory鎖來實現這一點。Advisory鎖允許您利用 PostgreSQL 在內部使用的相同鎖定引擎來實現您自己的應用程式定義的目的。
 

用例 3:釋出/訂閱
我把最酷的例子留到最後:將事件推送到您的活躍客戶。例如,假設您需要通知使用者他們有新訊息可供閱讀。或者,您可能希望在資料可用時將資料流式傳輸到客戶端。通常,Web 套接字是這些事件的傳輸層,而 Redis 作為 Pub/Sub 引擎。
但是,從版本 9 開始,PostgreSQL 也透過LISTENandNOTIFY語句提供了此功能。任何 PostgreSQL 客戶端都可以訂閱 ( LISTEN) 到一個特定的訊息通道,它只是一個任意字串。當任何其他客戶端NOTIFY在該頻道上傳送訊息 ( ) 時,所有其他訂閱的客戶端都會收到通知。或者,可以附加一條小訊息。
如果您碰巧使用 Rails 和 ActionCable,那麼使用 PostgreSQL 甚至是開箱即用的。

 

相關文章