通常來講,事務是必須知足4個條件(ACID)::原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。html
從標題來看,既然都是事務,那之間有什麼區別?來一一解開,先從兩個數據庫說去。mysql
MySQL 屬於 關係型數據庫
, Redis 屬於 非關係型數據庫
,二者對事務有着不一樣的解釋。redis
[1] Redis 事務能夠一次執行多個命令, 而且帶有如下兩個重要的保證:sql
一個事務從開始到執行會經歷如下三個階段:shell
單個 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
的值是什麼。app
127.0.0.1:6379> get tr_1 "233"
經過 get
命令來的tr_1
內容仍是 233 ,並無變,那再看一下其餘的。ide
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
maxmemory
directive).//
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 只有 InnoDB 引擎
支持 事務,在啓用 MySQL 事務以前須要先停掉自動提交
user
列 | 類型 | 註釋 |
---|---|---|
id | int(11) 自動增量 | 主鍵ID |
money | int(11) [0] | 金錢 |
title | varchar(500) NULL | 稱呼 |
在這裏來模擬一個轉帳的操做:A
給B
轉100元
。
步驟解析 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 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 支持手動回滾,實際開發過程當中能夠自行手動對已提交的操做進行回滾操做,更加友好。