MySQL 和 Redis 事務的對比

簡言

一般來說,事務是必須滿足4個條件(ACID)::原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。

從標題來看,既然都是事務,那之間有什麼區別?來一一解開,先從兩個數據庫說去。

MySQL 屬於 關係型數據庫 , Redis 屬於 非關係型數據庫,兩者對事務有着不同的解釋。

Redis

[1] Redis 事務可以一次執行多個命令, 並且帶有以下兩個重要的保證:

  • 批量操作在發送 EXEC 命令前被放入隊列緩存。
  • 收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其餘的命令依然被執行。
  • 在事務執行過程,其他客戶端提交的命令請求不會插入到事務執行命令序列中。

一個事務從開始到執行會經歷以下三個階段:

  • 開始事務。
  • 命令入隊。
  • 執行事務。

單個 Redis 命令的執行是原子性的,但 Redis 沒有在事務上增加任何維持原子性的機制,所以 Redis 事務的執行並不是原子性的。

事務可以理解爲一個打包的批量執行腳本,但批量指令並非原子化的操作,中間某條指令的失敗不會導致前面已做指令的回滾,也不會造成後續的指令不做。

操作錯誤

看着有點兒繞口,那就實際執行一下 看一下結果。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set tr_1 233
QUEUED
127.0.0.1:6379> lpush tr_1 666
QUEUED
127.0.0.1:6379> set tr_2 888
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK

在上面的事務中,設置了一個 key 爲 tr_1 的字符串數據,然後又通過 lpush 來添加元素,這很明顯是錯誤的操作方式,當我們提交事務候出現了一個操作錯誤,這時候我們來看看 tr_1 的值是什麼。

127.0.0.1:6379> get tr_1
"233"

通過 get 命令來的tr_1 內容還是 233 ,並沒有變,那再看一下其他的。

127.0.0.1:6379> keys *
1) "tr_2"
2) "tr_1"
127.0.0.1:6379> get tr_2
"888"
127.0.0.1:6379>

這裏可以看到 tr_2 存在,並打印了值,這時候我們發現,即使出現了操作錯誤 ,但是錯誤並沒有致使執行停止,錯誤之後的語句也執行了併成功執行,似乎符合上面提到的 中間某條指令的失敗不會導致前面已做指令的回滾,也不會造成後續的指令不做。

語法錯誤

NO~,這時候還有另外一種情況 語法錯誤

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set tr_1 233
QUEUED
127.0.0.1:6379> lpush tr_1 666
QUEUED
127.0.0.1:6379> set
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> set 233
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> set tr_2 888
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
(empty list or set)

當我們執行到 set時沒有給任何參數,第二次執行時故意少給了一個參數。可以看到報了 語法錯誤,最後提交事務,也告訴了我們事務因爲錯誤被丟失了,接着用 keys *檢索發現確實如此。

文檔釋義

這裏可以官方文檔中提到的

Errors inside a transaction

// 在執行過程中 可能會遇到兩種錯誤命令錯誤。

During a transaction it is possible to encounter two kind of command errors:

// 1.命令無法進入隊列 ,比如 :參數數量錯誤,命令名錯誤...,或者某些關鍵錯誤 如內存不足

  • A command may fail to be queued, so there may be an error before EXEC is called. For instance the command may be syntactically wrong (wrong number of arguments, wrong command name, ...), or there may be some critical condition like an out of memory condition (if the server is configured to have a memory limit using the maxmemorydirective).

// 2. 對鍵進行錯誤的操作 如上面的 對字符串使用 lpush

  • A command may fail after EXEC is called, for instance since we performed an operation against a key with the wrong value (like calling a list operation against a string value).

// 客戶端檢查鍵入的命令,大多數時候會在調用 exec 前發現第一類錯誤,如果命令執行返回來 QUEUED 則表示命令正常進入隊列,否則錯誤,大多數情況下客戶端會終止放棄這個事務。

Clients used to sense the first kind of errors, happening before the EXEC call, by checking the return value of the queued command: if the command replies with QUEUED it was queued correctly, otherwise Redis returns an error. If there is an error while queueing a command, most clients will abort the transaction discarding it.

關於 Redis 暫時看到這裏 接下來看到 MySQL

MySQL

衆所周知,MySQL 只有 InnoDB 引擎支持 事務,在啓用 MySQL 事務之前需要先停掉自動提交

測試表結構 user

類型 註釋
id int(11) 自動增量 主鍵ID
money int(11) [0] 金錢
title varchar(500) NULL 稱呼

在這裏來模擬一個轉賬的操作:AB100元

步驟解析 A+100 元,B -100元,即兩步雖然很簡單,簡單走一下流程。

正常執行

可以看到,沒有問題,那麼我們從中人爲的製造一些問題呢?

操作錯誤

類型 註釋
id int(11) 自動增量
money int(11) unsigned [0]
title varchar(500) NULL

這裏我們把 money 字段變成了無符號,即不能小於 0,並且,調整數據庫中的數據如下。

`SELECT * FROM `user` LIMIT 50` (0.000 秒) 
修改 id money title
編輯 1 10000 A
編輯 2 0 B

接着執行下面的 SQL

select version();
SET AUTOCOMMIT=0;
begin;
select * from user where title in ('A','B') for update;
update user set  money = money + 1000 where title = 'A';
update user set money = money - 1000 where title = 'B';
select * from user where title in ('A','B');
commit;

操作錯誤

問題出現了,這裏報出了錯誤,但是可以看到 前面的 SQL 已經是已執行的了,結果已經發生了變化,從這裏看,似乎和 Redis 的處理差不多,除了錯誤之後語句繼續執行。但是 值的注意的是, 在我們實際開發中,這種情況程序會直接拋出異常,以供我們在 catch 塊中執行 rollback ,以回滾操作確保數據完整,即使是單獨使用 MySQL 命令行 我們也可以用存儲過程來對異常進行回滾。

語法錯誤

剛剛看到 Redis 當遇到 語法錯誤 時會自動丟棄事務,阻止提交,那 MySQL 呢?

圖片描述

答案:不會,MySQL 在順序執行時,如果未對異常進行處理,總會將成功執行的的提交,而不會觸發自動終止,但是我們可以在程序執行時進行放棄提交。

Redis 爲什麼沒有回滾?

Redis 的官方文檔給出了這樣的解釋

​ // 只有在使用錯誤的語法調用時纔會失敗Redis命令(並且在命令排隊期間無法檢測到問題),或者對於持有錯誤數據類型的鍵,Redis命令可能會失敗:這意味着實際上失敗的命令是編程錯誤的結果,以及在開發過程中很可能檢測到的一種錯誤,而不是在生產中。

  • Redis commands can fail only if called with a wrong syntax (and the problem is not detectable during the command queueing), or against keys holding the wrong data type: this means that in practical terms a failing command is the result of a programming errors, and a kind of error that is very likely to be detected during development, and not in production.

// Redis內部簡化且速度更快,因爲它不需要回滾的能力。

  • Redis is internally simplified and faster because it does not need the ability to roll back.

總結

數據庫 自動回滾條件 操作錯誤 語法錯誤
MySQL
Redis

但是 MySQL 支持手動回滾,實際開發過程中可以自行手動對已提交的操作進行回滾操作,更加友好。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章