《一塊兒學mysql》2

事務
 
爸媽讓往他們銀行卡里轉點兒錢,但是我這錢全在支付寶裏,爸媽又沒有支付寶,只能從支付寶裏轉給他
們了,假如轉帳過程當中,支付寶扣款成功了,可是銀行系統崩潰了,錢沒轉進去,這咋整?個人大洋就這樣
打水漂了?確定不能夠,爲了不這種狀況的發生,就用到了事務,在轉錢過程當中遇到了任何差錯,就回到
沒轉錢以前的狀態,這個就叫作事務
 
事務四大特性(ACID)
如下特性純屬我的理解
原子性(Atomicity):轉帳前 -> 轉帳  -> 轉帳成功,以上述轉帳爲例,轉帳這個動做包括【從個人支付寶扣
除money,在我爸媽的卡上增長money】,中括號裏的內容要麼所有執行(轉帳成功),若是沒有所有執行就
回到轉帳前的狀態(跟沒執行一個效果),不能停留在轉帳的中間過程——個人支付寶扣了錢,爸媽銀行卡沒
多錢
 
一致性(Consistency):個人理解是能量守恆,轉帳先後,個人支付寶金額+我爸媽卡內金額是一致的
 
隔離性(Isolation):這個通常用在併發,兩個線程的操做會互相影響的狀況下,隔離性又分爲若干個隔離級
別,下面具體討論
 
永久性(Durability):只要事務提交了,就成了事實。
 
隔離級別
在說隔離級別以前先來講幾個概念
 
髒讀:讀到了另外一個事務未提交的更改
 
例如 
 事務一:包工頭給農民工轉帳
 事務二:農民工查看工資
 
 事務一:開始事務
 事務一:包工頭給農民工轉帳1000
 事務二:開始事務
 事務二:農民工查看帳戶,多了1000塊
 事務二:提交
 事務一:包工頭回滾,轉帳失敗
 
不可重複讀:一個事務對同一記錄的讀取結果不一致,由於另外一個事務更新了該記錄,並提交
 
例如
 事務一:查看賓館8301 的狀態
 事務二:預訂8301房間
 
 事務一:開始事務
 事務一:查看8301狀態,未預約
 事務二:開始事務
 事務二:預約8301房間
 事務二:提交
 事務一:再次查看8301 狀態,被預約
 
幻讀:一個事務執行同一個查詢結果不同,由於另外一事務插入了新的記錄,並提交
 
例如
 事務一:統計網站的註冊用戶數
 事務二:註冊新的用戶
 
 事務一:開始事務
 事務一:查看註冊用戶數爲10000
 事務二:開始事務
 事務二:新增一個用戶(插入一條記錄)
 事務二:提交
 事務一:查看註冊用戶數爲10001
 
四種隔離級別
 
是否容許髒讀
是否不可重複讀
是否幻讀
read uncommitted
read committed
repeatable read
serializable
事務的使用:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> insert into test values(2);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [jason]> select * from test;
+------+
| i    |
+------+
|    1 |
|    2 |
+------+
2 rows in set (0.00 sec)
 
MariaDB [jason]> rollback;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select * from test;
+------+
| i    |
+------+
|    1 |
+------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test values(2);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select * from test;
+------+
| i    |
+------+
|    1 |
|    2 |
+------+
2 rows in set (0.00 sec)
 
mysql默認自動開啓事務,能夠經過begin 手動開啓事務,若是想要回到begin以前的狀態,則rollback,
操做完成後記得commit,不然推出窗口會就等同rollback 了
 
mysql 中的鎖 
 
行級別鎖只對事務安全的表(innodb,bdb)有效
 
for update 行級鎖
首先咱們先建一張表,插入兩條數據
 
create table test(
f1 int,
f2 char(10),
index (f1)
)engine=innodb;
 
insert into test (f1) values
(1),
(2);
 
假設咱們如今有個需求,f1 必須是惟一的,不可重複,每次插入只能插入當前最大值加 1,假設有兩個窗
口各有一個事務,在讀取最大值並插入
 
窗口1:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test;
+---------+
| max(f1) |
+---------+
|       2 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test (f1) values(3);
Query OK, 1 row affected (0.00 sec)
 
窗口2:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test;
+---------+
| max(f1) |
+---------+
|       2 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test (f1) values(3);
Query OK, 1 row affected (0.00 sec)
 
窗口1:
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.01 sec)
 
窗口2:
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.01 sec)
 
如今查看數據:
MariaDB [jason]> select * from test;
+------+------+
| f1   | f2   |
+------+------+
|    1 | NULL |
|    2 | NULL |
|    3 | NULL |
|    3 | NULL |
+------+------+
4 rows in set (0.00 sec)
 
結果咱們插入了重複的數據,這與咱們的要求相背離了,mysql 提供了鎖,能夠解決這個問題
咱們先把錯誤數據刪掉
 
MariaDB [jason]> delete from test where f1=3;
Query OK, 2 rows affected (0.00 sec)
 
咱們能夠用一個 for update 鎖來達到這個目的, 只能有一個會話能夠擁有這個鎖,當另外一個會話也申請這個鎖
時,會暫時卡住,直到前一個會話commit  釋放鎖以後,
 
窗口1:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test for update;
+---------+
| max(f1) |
+---------+
|       2 |
+---------+
1 row in set (0.00 sec)
 
窗口2:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test;
+---------+
| max(f1) |
+---------+
|       2 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> select max(f1) from test for update;
窗口2 暫時沒有查詢結果,要等到 窗口1 commit 以後才行
 
窗口1:
MariaDB [jason]> insert into test (f1) values (3);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.01 sec)
 
i窗口2:
+---------+
| max(f1) |
+---------+
|       3 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test (f1) values (4);
Query OK, 1 row affected (0.00 sec)
 
MariaDB [jason]> commit;
Query OK, 0 rows affected (0.01 sec)
窗口2 的等待可能會超時,報錯:Lock wait timeout exceeded;
 
咱們再來看看
MariaDB [jason]> select * from test;
+------+------+
| f1   | f2   |
+------+------+
|    1 | NULL |
|    2 | NULL |
|    3 | NULL |
|    4 | NULL |
+------+------+
4 rows in set (0.00 sec)
 
共享讀鎖:lock in share mode 行級鎖
若是一個事務正在修改f1 的值,而另外一個窗口想讀取有關f1 的最新值,能夠用共享讀鎖
 
窗口1:
MariaDB [jason]> begin;
Query OK, 0 rows affected (0.00 sec)
 
MariaDB [jason]> select max(f1) from test;
+---------+
| max(f1) |
+---------+
|       4 |
+---------+
1 row in set (0.00 sec)
 
MariaDB [jason]> insert into test (f1) values(5);
Query OK, 1 row affected (0.00 sec)
 
窗口2;
 
MariaDB [jason]> select max(f1) ,f2 from test lock in share mode;
上面語句會阻塞直到 窗口1 commit;一樣會超時跑異常
 
表級鎖
 
讀鎖
只能對錶進行讀操做,不能進行寫操做,寫操做被鎖
 
窗口1:申請test 表的讀鎖,在窗口1 中能夠讀,寫test
窗口2:容許讀test,可是寫操做會阻塞,直到窗口1 unlock
 
 
寫鎖
上鎖的那段時間沒有讀或寫操做
上鎖的窗口能夠執行插入和insert 操做,其餘窗口執行的命令會遇到阻塞
 
lock table 不是事務安全的,在鎖表以前會提交所有的活動事務,
在更新比較頻繁的表中應該儘可能避免表級鎖,以免擁堵
相關文章
相關標籤/搜索