先來舉一個在某些應用場景下會出現數據不一致的例子,固然存儲引擎是InnoDB(至於爲何,後面再告訴你)。html
電商平臺常見的下單場景:mysql
通常商品表(goods)有基本的四個字段,id(主鍵),goods_name (商品名),goods_status是商品狀態(下架仍是在售),goods_stock(庫存)。sql
用戶在購買商品id爲1的商品,雖然通常展現層會篩選出在售狀態的商品,在嚴謹的流程中咱們還須要判斷一下是否在售,商品的庫存數量等條件是否正常,來避免出現問題。但併發狀況下,可能會出現用戶在購買將要付費的時候,商品管理人員搶先把商品下架,就會出現不一致了。數據庫
select goods_status,goods_stock from goods where id=1; //查出商品狀態和庫存
inset into orders (goods_id,goods_count) values (1,3); //若是庫存和狀態正常,把購買的商品和數量寫入訂單表
接下來還會有付費減庫存等操做......併發
這裏商品管理人員忽然對商品的狀態或者庫存作了調整測試
update goods set goods_status = 0 where id =3; //修改狀態爲例//
在上面的場景中,商品信息從查詢出來到修改,中間有一個處理訂單的過程。那麼可使用悲觀鎖來解決此問題。spa
使用悲觀鎖的原理就是,當咱們在查詢出goods信息後就把當前的數據鎖定,直到咱們修改完畢後再解鎖。那麼在這個過程當中,由於goods被鎖定了,就不會出現有第三者來對其進行修改了。rest
要注意的是,使用悲觀鎖,必須關閉Mysql的自動提交機制:http://www.cnblogs.com/wt645631686/p/7986696.htmlcode
//0.開始事務
begin;/begin work;/start transaction; (三者選一就能夠) //1.查詢出商品信息
select goods_status from goods where id=1 for update; //2.根據商品信息生成訂單
insert into orders (goods_id,goods_count) values (3,5); //3.修改商品status爲2
update goods set status=2; //4.提交事務
commit;/commit work;
拿上面的實例來講,當我執行select status from goods where id=3 for update;後。我在另外的事務中若是再次執行select status from goods where id=3 for update;則第二個事務會一直等待第一個事務的提交,此時第二個查詢處於阻塞的狀態,可是若是我是在第二個事務中執行select status from goods where id=3;則能正常查詢出數據,不會受第一個事務的影響。htm
舉例說明
數據庫表goods,包括id,status,name三個字段,id爲主鍵,數據庫中記錄以下;
mysql> select * from goods; +----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
| 2 | 1 | 裝備 |
+----+--------+------+
注:爲了測試數據庫鎖,我使用兩個console來模擬不一樣的事務操做,分別用console一、console2來表示。
例1: (明確指定主鍵,而且有此數據,row lock)
console1:查詢出結果,可是把該條數據鎖定了
mysql> select * from goods where id=1 for update; +----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
+----+--------+------+
console2:查詢被阻塞
mysql> select * from goods where id=1 for update;
console2:若是console1長時間未提交,則會報錯
mysql> select * from goods where id=1 for update; ERROR 1205 : Lock wait timeout exceeded; try restarting transaction
例2: (明確指定主鍵,若查無此數據,無lock)
console1:查詢結果爲空
mysql> select * from goods where id=3 for update; Empty set
console2:查詢結果爲空,查詢無阻塞,說明console1沒有對數據執行鎖定
mysql> select * from goods where id=3 for update; Empty set
例3: (無主鍵,table lock)
console1:查詢name=道具 的數據,查詢正常
mysql> select * from goods where name='道具' for update; +----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
+----+--------+------+
console2:查詢name=裝備 的數據,查詢阻塞,說明console1把表給鎖住了
mysql> select * from goods where name='裝備' for update;
console2:若console1長時間未提交,則查詢返回爲空
mysql> select * from goods where name='裝備' for update; Query OK, -1 rows affected
例4: (主鍵不明確,table lock)
console1:查詢正常
mysql> begin; Query OK, 0 rows affected mysql> select * from goods where id>0 for update; +----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
| 2 | 1 | 裝備 |
+----+--------+------+
console2:查詢被阻塞,說明console1把表給鎖住了
mysql> select * from t_goods where id>1 for update;
例5: (主鍵不明確,table lock)
console1:
mysql> begin; Query OK, 0 rows affected mysql> select * from t_goods where id<>1 for update; +----+--------+------+
| id | status | name |
+----+--------+------+
| 2 | 1 | 裝備 |
+----+--------+------+
console2:查詢被阻塞,說明console1把表給鎖住了
mysql> select * from t_goods where id<>2 for update;
console1:提交事務
mysql> commit; Query OK, 0 rows affected
console2:console1事務提交後,console2查詢結果正常
mysql> select * from t_goods where id<>2 for update; +----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
+----+--------+------+
以上就是關於數據庫主鍵對MySQL鎖級別的影響實例,須要注意的是,除了主鍵外,使用索引也會影響數據庫的鎖定級別
舉例:
咱們修改goods表,給status字段建立一個索引
修改id爲2的數據的status爲2,此時表中數據爲:
mysql> select * from goods; +----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
| 2 | 2 | 裝備 |
+----+--------+------+
例6: (明確指定索引,而且有此數據,row lock)
console1:
mysql> select * from goods where status=1 for update; +----+--------+------+
| id | status | name |
+----+--------+------+
| 1 | 1 | 道具 |
+----+--------+------+
console2:查詢status=1的數據時阻塞,超時後返回爲空,說明數據被console1鎖定了
mysql> select * from goods where status=1 for update; Query OK, -1 rows affected
console2:查詢status=2的數據,能正常查詢,說明console1只鎖住了行,未鎖表
mysql> select * from goods where status=2 for update; +----+--------+------+
| id | status | name |
+----+--------+------+
| 2 | 2 | 裝備 |
+----+--------+------+
例7: (明確指定索引,若查無此數據,無lock)
console1:查詢status=3的數據,返回空數據
mysql> select * from t_goods where status=3 for update; Empty set
console2:查詢status=3的數據,返回空數據
Sql代碼 收藏代碼
mysql> select * from t_goods where status=3 for update; Empty set
完畢。。。