Redis-事務(wù)與單線程事件處理模型

Redis的事務(wù)是基于單線程的兩階段不完全事務(wù)(主要是不支持原子性),它僅僅是保證事務(wù)里的操作會被連續(xù)獨(dú)占的執(zhí)行。因?yàn)槭?strong>單線程架構(gòu),在執(zhí)行完事務(wù)內(nèi)所有指令前是不可能再去同時執(zhí)行其他客戶端的請求的。

0- 為什么是單線程

Redis作者追求簡單,簡單才不會出錯,使得代碼不用處理平時最讓人頭痛的并發(fā)而大幅簡化,作者認(rèn)為CPU不是瓶頸,內(nèi)存與網(wǎng)絡(luò)帶寬才是,當(dāng)top看到單個CPU 100%時,單核CPU稱為瓶頸,此時就是垂直擴(kuò)展的時候了。

1- Redis事務(wù)ACID

我們知道Mysql等傳統(tǒng)關(guān)系型數(shù)據(jù)庫一般都滿足ACID,下面來看一看Redis是否滿足ACID

  1. 原子性(Atomicity)

它不保證原子性——所有指令同時成功或同時失敗,只有決定是否開始執(zhí)行全部指令的能力,沒有執(zhí)行到一半進(jìn)行回滾的能力。當(dāng)系統(tǒng)突然宕機(jī),Mysql也能根據(jù)binlog恢復(fù),而Redis不可以。

  1. 一致性(Consistency)

redis 事務(wù)在執(zhí)行過程中發(fā)生錯誤或進(jìn)程被終結(jié),都能保證數(shù)據(jù)的一致性(Consistency)。因?yàn)閞edis 使用單線程串行方式來執(zhí)行事務(wù)的,在執(zhí)行事務(wù)期間不會對事務(wù)進(jìn)行中斷(一致性)。

  1. 隔離性(Isolation)

它沒有隔離級別的概念,因?yàn)槭聞?wù)提交前任何指令都不會被實(shí)際執(zhí)行,也就不存在”事務(wù)內(nèi)的查詢要看到事務(wù)里的更新,在事務(wù)外查詢不能看到”這個讓人萬分頭痛的問題,所以滿足隔離性(Isolation)。

  1. 持久性(Durability)

但當(dāng)redis 服務(wù)器使用AOF 持久化模式并appendfsync 設(shè)置為always 時,程序執(zhí)行操作命令后會調(diào)用sync 函數(shù)將數(shù)據(jù)保存到硬盤里,因此redis 事務(wù)也可以具有持久性(Durability)。

2- 事務(wù)實(shí)現(xiàn):Mysql vs Redis

Mysql
  1. 悲觀鎖

是通過select * from table where for update將數(shù)據(jù)加鎖,導(dǎo)致其他線程或事務(wù)不能更新該數(shù)據(jù)。

  1. 樂觀鎖
  • InnoDB基于系統(tǒng)版本號實(shí)現(xiàn)樂觀鎖(只在可重復(fù)讀和提交讀隔離級別下工作),每行記錄后面保存了隱藏兩個列,一個是創(chuàng)建或者修改這條記錄的時候當(dāng)前系統(tǒng)的版本號(每次操作都將版本號加1),一個是刪除此行記錄是當(dāng)前系統(tǒng)的版本號,一個記錄修改,一個記錄刪除標(biāo)識。
  • 當(dāng)讀取數(shù)據(jù)時,將版本標(biāo)識的值一同讀出,數(shù)據(jù)每更新一次,同時對版本標(biāo)識進(jìn)行更新。當(dāng)我們提交更新的時候,判斷數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息與第一次取出來的版本標(biāo)識進(jìn)行比對,如果數(shù)據(jù)庫表當(dāng)前版本號與第一次取出來的版本標(biāo)識值相等,則予以更新,否則認(rèn)為是過期數(shù)據(jù),事務(wù)失敗。
Redis

redis 是通過watch 命令監(jiān)控?cái)?shù)據(jù)是否發(fā)生變化實(shí)現(xiàn)樂觀鎖的。當(dāng)watch 的對象被更改,比如某個list已被別的客戶端push/pop過了,會導(dǎo)致事務(wù)放棄執(zhí)行。

3- 事務(wù):Mysql vs Redis

不同場景下Mysql(InnoDB存儲引擎)事務(wù)與Redis事務(wù)的比較

背景:tom1 賬戶當(dāng)前有1000元,tom2 賬戶當(dāng)前有500元

場景1:使用事務(wù)從tom1轉(zhuǎn)100元到tom2
  1. mysql 開啟事務(wù)后事務(wù)中的sql 語句在commit 之前就已經(jīng)執(zhí)行了sql 語句的(同一事務(wù)內(nèi)可重復(fù)讀,InnoDB默認(rèn)隔離級別),只是并未真正提交到數(shù)據(jù)庫。

  2. redis 使用multi 開啟事務(wù)后,編寫的sql 語句都進(jìn)入queue 隊(duì)列中(Redis沒有隔離級別的概念,sever單線程模式,不會并發(fā)讀),待執(zhí)行exec 提交事務(wù)時才一次性按進(jìn)入queue 隊(duì)列的順序提交到數(shù)據(jù)庫。

  3. 同樣針對回滾,mysql 執(zhí)行rollback 會將提交的數(shù)據(jù)回滾,redis 因?yàn)闆]有提交到數(shù)據(jù),使用discard 只是單純?nèi)∠趒ueue 中的sql 語句。

場景2: 在執(zhí)行tom1 向tom2 轉(zhuǎn)過程中100元的過程中使用了語法錯誤的sql。
  1. mysql 事務(wù)即使遇到錯誤的語句也會提交正確的sql 到數(shù)據(jù)庫(正確語句提交,發(fā)生錯誤的語句拋出異常,但是不影響事務(wù)的提交),需要程序員控制當(dāng)遇到語句異常時進(jìn)行回滾。

  2. redis 與mysql 不同,提交事務(wù)時會先檢查queue 中有語法是否有錯誤,如果有語法錯誤則會discard 整個事務(wù)中操作命令語句。

場景3: 在執(zhí)行tom1 向tom2 轉(zhuǎn)過程中100元的過程中使用了錯誤的執(zhí)行對象。


mysql 和redis 事務(wù)當(dāng)遇到操作對象類型不正確的時候都會提交執(zhí)行事務(wù),但是Mysql可以檢測這個錯誤然后通知客戶端,如果客戶端要求錯誤回滾,則能將已經(jīng)執(zhí)行操作回滾,而Redis沒有這個權(quán)限:只有決定是否開始執(zhí)行全部指令的能力,沒有執(zhí)行到一半進(jìn)行回滾的能力。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容