iT邦幫忙

2024 iThome 鐵人賽

DAY 17
0

今天的內容主要對針對Redis再進行一些討論,主要的大綱如下:

  • Redis Connection Pool 與 SQL Connection Pool
  • Redis 與 SQL ACID
  • Redis Transaction 與 SQL Transaction
  • Redis PipeLine

那麼我們開始。

SQL Connection Pool 是什麼? SQL Connection Pool 是指 今天Web應用程式透過DAO朝DB發出請求時,應用程式與DB就會建立一次連線,連線會隨著請求的回傳而中斷。而在大量請求湧入後端時,應用程式就不斷地對資料庫重複建立連線、斷開連線的行為。因為不斷建立又斷開的開銷很大,所以SQL Connection Pool的概念應運而生。

Connection Pool是在應用程式與資料庫之間建立一個物件池,當今天請求結束後,不讓連線立刻關閉,並在一些條件下 ( 例如連線字串相同的狀況下) 讓這些連線可以被重複利用,以此減少對SQL的系統負擔。目前各家SQL都有使用這樣的方法。

Redis Connection Pool 也基於同樣的概念,除此之外,Redis 也可以設定 Redis的最大連線數等等,而如果在最大連線數情況下,請求將被迫等候,超過最大容忍時間後則會Timeout。在一些高併發的場景中會被使用。

主要使用Connection Pool的好處如下:

  1. 效能提升,省去了重複大量建立連線與斷開連線的開銷。
  2. 控制資源消耗,連線池可以控制容許的最大請求,避免大流量衝擊DB把Database打掛。

SQL ACID的 原子性,指的是一筆對資料庫的交易必須是全數成功或是全數失敗、若執行到中途遭遇失敗則要觸發資料庫的Rollback,確保不會發生部分成功部分失敗的問題。

而Redis是單執行緒的,也就是說對同一筆資料同時間只會有一個Process去存取它,其他的Process是無法同時存取的。雖然操作是獨立不受影響的,但Redis因為不支援Rollback,所以結論是不符合原子性。

舉例來說,在Redis中,一個交易的生命週期如下:

  1. 利用MULTI指令開啟一個交易,一個交易中可以包含多個對Redis的操作。
  2. 把對Redis的操作發送給Redis。
  3. 使用EXEC指令觸發對Redis的操作。

在Redis中,錯誤分為兩種,一種是在EXEC指令執行前,因為尚未執行,所以可以確保原子性 ( 失敗就是全部失敗 )。另一種是在EXEC後,Redis會除了發生錯誤的部分不執行外,其他都執行,進而破壞了原子性。

Redis中有交易,但這個交易不同於SQL的交易,Redis的交易主要是為了把多個請求裝在同一個交易中一次執行,而不是確保符合原子性。

再來,因為Redis存放的資料格式可以隨意被更動,完整性可被更改,所以Redis不支援一致性。

隔離性的部分,Redis可以透過WATCH指令去檢測的值有沒有被更動,簡言之就是使用樂觀鎖去確保交易間的隔離性。

持久性需要實作功能才能確保符合持久性,如: RDB 和 AOF模式

總之,Redis在很多層面不同於傳統的SQL,結論是不太適用ACID的原則。

在RedisTemplate,針對多個命令的執行,有不少種的方法,為了避免混淆,我稍微研究了一下,並分出他們的差別。

redisTemplate.executeWithStickyConnection(connection -> {
    connection.set(redisTemplate.getStringSerializer().serialize("key1"), 
                   redisTemplate.getStringSerializer().serialize("value1"));
    connection.set(redisTemplate.getStringSerializer().serialize("key2"), 
                   redisTemplate.getStringSerializer().serialize("value2"));
    return null;
});

redisTemplate.execute((RedisCallback<Object>) connection -> {
    connection.multi(); // 開啟事務
    connection.set(redisTemplate.getStringSerializer().serialize("key1"), 
                   redisTemplate.getStringSerializer().serialize("value1"));
    connection.set(redisTemplate.getStringSerializer().serialize("key2"), 
                   redisTemplate.getStringSerializer().serialize("value2"));
    connection.exec(); // 提交事務
    return null;
});

redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
    connection.set(redisTemplate.getStringSerializer().serialize("key1"), 
                   redisTemplate.getStringSerializer().serialize("value1"));
    connection.set(redisTemplate.getStringSerializer().serialize("key2"), 
                   redisTemplate.getStringSerializer().serialize("value2"));
    return null;
});
  • executeWithStickyConnection 確保了多個命令在同一個連線中被執行。
  • multi 確保了多個命令在同一個交易中被執行
  • executePipelined 批次送出多個交易,再一次接收回應。

以上是一些Redis的特性,Redis雖然使用起來很簡便,但是學問遠比我想像得要來得多,只能說學無止盡…

今天的部分就到此為止了,明天見吧。

參考資源:

https://bear-1111.medium.com/redis-acid-特性討論-566c7f7bcba1

https://blog.csdn.net/yangbindxj/article/details/123475569

cpp_backend_awsome_blog/【NO.375】你真的懂Redis与MySQL双写一致性如何保证吗?.md at main · 0voice/cpp_backend_awsome_blog (github.com)


上一篇
[DAY 22] Redis快速入門介紹
下一篇
[DAY 24] Spring 常見快取種類
系列文
週日時在做什麼?有沒有空?可以來寫SpringBoot嗎?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言