MySql鎖和事務隔離級別

前言

MySql索引底層數據結構和算法:https://blog.csdn.net/yhl_jxy/article/details/88392411

MySql explan執行計劃詳解:https://blog.csdn.net/yhl_jxy/article/details/88570154

MySql 索引優化原則:https://blog.csdn.net/yhl_jxy/article/details/88636685

一 鎖概述

1、什麼是鎖?

鎖是計算機協調多個進程或線程併發訪問某一資源的機制。

在數據庫中,鎖主要用於解決併發訪問時保證數據的一致性和有效性。

2、鎖的分類?

1)從性能上分爲樂觀鎖和悲觀鎖

2)從對數據庫操作的類型分爲讀鎖和寫鎖(都屬於悲觀鎖)。

    讀鎖(共享鎖):多個讀操作可以同時對同一份數據進行讀而不會互相影響;

    寫鎖(排它鎖):當前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖;

3)從對數據操作的粒度分爲表鎖和行鎖

二 表鎖

1、表鎖

開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。

2、手動加鎖語法

lock table 表名1 read(write),表名2 read(write);

3、表鎖演示

3.1 腳本準備

drop table if exists testlock;

CREATE TABLE testlock (
 id INT (11) NOT NULL AUTO_INCREMENT,
 userName VARCHAR (20) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE = MyISAM DEFAULT CHARSET = utf8;

INSERT INTO testlock (id, userName) VALUES (1, 'a');
INSERT INTO testlock (id, userName) VALUES (2, 'b');
INSERT INTO testlock (id, userName) VALUES (3, 'c');

3.2 表上加讀鎖演示

開兩個MySql終端窗口。

SessionA SessionB 說明
mysql> lock table testlock read;
Query OK, 0 rows affected (0.00 sec)
  SessionA給testlock加讀鎖。
  mysql> select * from testlock;
+----+----------+
| id | userName |
+----+----------+
|  1 | a        |
|  2 | b        |
|  3 | c        |
+----+----------+
3 rows in set (0.00 sec)
SessionB可以對testlock進行查詢,
說明表上加讀鎖,別的session可以讀取
數據,不受表讀鎖的影響。
  mysql> insert into testlock values(4, 'd'); SessionB做插入操作,插入語句
被阻塞,說明表上的讀鎖阻塞寫操作,即insert,update,delete操作。
mysql> unlock table;
Query OK, 0 rows affected (0.00 sec)
  SessionA釋放表上的讀鎖。
  mysql> insert into testlock values(4, 'd');
Query OK, 1 row affected (532.36 sec)
 
mysql> select * from testlock;
+----+----------+
| id | userName |
+----+----------+
|  1 | a        |
|  2 | b        |
|  3 | c        |
|  4 | d        |
+----+----------+
4 rows in set (0.00 sec)
當SessionA釋放表鎖後,SessionB
的insert插入成功。如果再次查詢,
可以看到成功插入4這條數據。

表上加讀鎖會阻塞寫操作,但是不會阻塞讀操作。

3.3 表上加寫鎖演示

SessionA SessionB 說明
mysql> lock table testlock write;
Query OK, 0 rows affected (0.00 sec)
  SessionA給testlock加寫鎖。
  mysql> select * from testlock; SessionB對testlock進行查詢被阻塞,
說明表上加寫鎖,別的session不能
進行讀操作,當然寫操作也是不行的。
mysql> unlock table;
Query OK, 0 rows affected (0.00 sec)
  SessionA釋放表上的寫鎖。
  mysql> select * from testlock;
+----+----------+
| id | userName |
+----+----------+
|  1 | a        |
|  2 | b        |
|  3 | c        |
|  4 | d        |
+----+----------+
4 rows in set (0.00 sec)
當SessionA釋放表鎖後,SessionB
的select查詢成功。

表上加寫鎖則會把讀操作和寫操作都阻塞;

4、表鎖總結

表上加讀鎖會阻塞寫操作,但是不會阻塞讀操作;

而表上加寫鎖則會把讀操作和寫操作都阻塞;

三 行鎖

行鎖偏向InnoDB存儲引擎,開銷大,加鎖慢,會出現死鎖,鎖定粒度最小,發生鎖衝突的概率最低,

併發度也最高。InnoDB與MYISAM有兩個最大的不同點:支持事務(TRANSACTION)和採用了行級鎖。

1、事務ACID

事務是由一組SQL語句組成的不可分割的最小邏輯單元。事務具有ACID屬性。

原子性(Atomicity) 

      事務是一個原子操作單元,整個事務中的所有操作要麼全部提交成功,要麼全部執行失敗,

對於一個事務的操作不可能只執行其中的一部分操作,這就是事務的原子性。

一致性(Consistent) 

      在事務開始和完成時,數據都必須保持一致狀態。這說明,事務裏面操作的數據要保證數據的完整性和正確性。

隔離性(Isolation) 

      通常來說,一個事務在未提交的時候,對另外一個事務來說數據是不可見的。

持久性(Durable) 

      事務提交之後,修改的數據就保存在數據庫中,就算機器崩掉了,數據也還存在。

2、併發事務處理存在的問題

更新丟失(Lost Update)

     當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,由於每個事務都不知道其他事務的存在,

就會發生丟失更新問題–最後的更新覆蓋了由其他事務所做的更新。

髒讀(Dirty Reads)

      A事務正在修改數據,事務B讀取到了事務A已經修改但尚未提交的數據,B還在這個數據基礎上做了操作。

此時,如果A事務回滾,B讀取的數據無效,並且拿無效的數據去做了別的操作,數據亂套了,不符合一致性要求。

這個現象就稱爲髒讀。

不可重讀(Non-Repeatable Reads) 

      A事務正在修改數據,事務B第一次讀取了事務A還沒修改的數據,B拿這個數據處理相應業務,過了會B又一次讀取

同樣一條A修改並提交事務的數據。B事務兩次拿到認爲是同樣的數據,其實不是同樣的數據,重複讀取數據導致

數據不一致問題,所以這種現象就叫做“不可重複讀”。

幻讀(Phantom Reads)

      一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,

這種現象就稱爲“幻讀”。

3、事務隔離級別

對“骯讀”、“不可重複讀”、“幻讀”通過不同的事務隔離級別來解決。

Read Uncommitted(未提交讀)

       該隔離級別,所有事務都可以看到其他事務未提交的執行結果。該隔離級別很少用於實際應用,

因爲它的性能也不比其他級別好多少。同時,會產生髒讀(Dirty Read)。

Read Committed(已提交讀)

       大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。一個事務只能看見已經提交事務所做的改變。

但是該隔離級別處理不了“重複讀取”的問題,所以也叫不可重複讀。

Repeatable Read(可重複讀)

       該隔離級別是MySQL的默認事務隔離級別,它確保同一事務的多個實例在併發讀取數據時,

會看到同樣的數據行。但是會產生幻讀 (Phantom Read)問題。

InnoDB和Falcon存儲引擎可以通過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決該問題。

Serializable(可串行化)

       這是最高的隔離級別,它通過強制事務排序,不會出現併發訪問數據,從而解決幻讀問題。

它是在每個讀的數據行上加上共享鎖,可能導致大量的超時現象和鎖競爭。

事務隔離級別 骯髒(Dirty Read)可能性 不可重複讀(Non-Repeatable Reads)可能性 幻讀(Phantom Reads)可能性
未提交讀(Read uncommited) Yes Yes Yes
提交讀(Read commited) No Yes Yes
可重複讀(Repeatable Read) No No Yes
可串行化(Serializable) No No No

數據庫的事務隔離越嚴格,併發副作用越小,但付出的代價也就越大,因爲事務隔離實質上就是使事務在一定程度

上“串行化”進行,這顯然與“併發”是矛盾的。同時,不同的應用對讀一致性和事務隔離程度的要求也是不同的,

比如許多應用對“不可重複讀"和“幻讀”並不敏感,可能更關心數據併發訪問的能力。

4、事務隔離級別演示

腳本準備

drop table if exists account;
create table account (
  id int(11) not null auto_increment,
  user_name varchar(225) default null comment '用戶名',
  balance bigint(11) default null comment '賬戶餘額',
  create_time datetime default current_timestamp comment '開戶時間',
  primary key (id)
) engine = InnoDB default charset = utf8 comment = '賬戶表';

insert into account (user_name, balance, create_time)
values ('ZhangSan', 1200, now()), ('LiSi', 3600, now()), ('WangWu', 2500, now());

未提交讀(Read uncommited)

在實戰中,不要使用該事務隔離級別,下面演示未提交讀現象。

SessionA SessionB 說明
mysql> set tx_isolation='read-uncommitted';
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='read-uncommitted';
Query OK, 0 rows affected (0.00 sec)
SessionA和SessionB事務隔離級別
設置爲未提交讀(Read uncommited)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
SessionA和SessionB開啓事務
mysql> select * from account;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1200 | 2019-03-20 11:21:31 |
|  2 | LiSi      |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu    |    2500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
3 rows in set (0.00 sec)
mysql> select * from account;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1200 | 2019-03-20 11:21:31 |
|  2 | LiSi      |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu    |    2500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
3 rows in set (0.00 sec)
SessionA和SessionB看到的數據一樣
mysql> update account set balance = balance - 200
where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
 
mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
  SessionA將id爲1的數據金額
從1200修改爲1000。
  mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
SessionB能夠讀到SessionA還沒有提交
的執行結果,balance=1000。
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
 
mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1200 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
 

SessionA將事務回滾,id爲1的balance

恢復爲1200。

  mysql> update account set balance = 1000 + 500 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
 
mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)

SessionB拿到1000,在賬戶上加500,
然後id爲1的balance變爲了1500,但是,
實際上SessionA已經回滾,數據變爲了
1200,SessionB加500,實際上應該是1700纔對,中間丟了200,因爲SessionB讀取的1000是髒數據,加完500就變成了1500。
這就是未提交讀隔離級別帶來的問題,

髒讀。

提交讀(Read commited)

爲了解決髒讀,將事務隔離級別提高到“提交讀”,下面演示“提交讀”事務隔離級別是怎麼回事,以及會帶來什麼問題。

SessionA SessionB 說明
mysql>commit;
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='read-committed';
Query OK, 0 rows affected (0.00 sec)
mysql>commit;
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='read-committed';
Query OK, 0 rows affected (0.00 sec)
在設置事務級別前,先commit一下,
預防出現你在幹別的,影響效果。
SessionA和SessionB事務隔離級別
設置爲提交讀(Read commited)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
SessionA和SessionB開啓事務
mysql> select * from account;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1500 | 2019-03-20 11:21:31 |
|  2 | LiSi      |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu    |    2500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
3 rows in set (0.00 sec)
mysql> select * from account;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1500 | 2019-03-20 11:21:31 |
|  2 | LiSi      |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu    |    2500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
3 rows in set (0.00 sec)
SessionA和SessionB看到的數據一樣
mysql> update account set balance = balance - 500
where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
 
mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
  SessionA將id爲1的數據金額
從1500修改爲1000。
  mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
SessionB能夠讀不到SessionA還沒有提交
的執行結果,balance還是1500。
解決了髒讀的問題。
mysql> select * from account;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
|  2 | LiSi      |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu    |    2500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
3 rows in set (0.00 sec)
  SessionA將事務提交,id爲1的balance持久化爲1000。
  mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
SessionB再次讀取id爲1的數據,balance拿到了SessionA修改並提交事務後的1000
    如果程序裏面通過balance的值判斷程序邏輯,會導致兩次走的邏輯不一樣,因爲兩次拿到不同的值。這就是“提交讀”帶來的問題,
因爲重複讀取數據,會拿到不同的值,可能會導致出現問題,所以,改級別也稱爲“不可重複讀”。

可重複讀(Repeatable Read)

爲了解決“不可重複讀”,將事務隔離級別提高到“可重複讀”,下面演示“可重複讀”事務隔離級別是怎麼回事,

以及會帶來什麼問題。

SessionA SessionB 說明
mysql>commit;
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
mysql>commit;
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
在設置事務級別前,先commit一下,
預防出現你在幹別的,影響效果。
SessionA和SessionB事務隔離級別
設置爲可重複讀(Repeatable Read)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
SessionA和SessionB開啓事務
mysql> select * from account;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
|  2 | LiSi      |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu    |    2500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
3 rows in set (0.00 sec)
mysql> select * from account;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
|  2 | LiSi      |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu    |    2500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
3 rows in set (0.00 sec)
SessionA和SessionB看到的數據一樣
mysql> update account set balance = balance - 500
where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
 
mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
  SessionA將id爲1的數據金額
從1000修改爲500。
  mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
SessionB能夠讀不到SessionA還沒有提交
的執行結果,balance還是1000。
解決了髒讀的問題。
mysql> select * from account;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    500 | 2019-03-20 11:21:31 |
|  2 | LiSi      |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu    |    2500 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
3 rows in set (0.00 sec)
  SessionA將事務提交,id爲1的balance持久化爲1000。
  mysql> select * from account where id = 1;
+----+-----------+---------+---------------------+
| id | user_name | balance | create_time         |
+----+-----------+---------+---------------------+
|  1 | ZhangSan  |    1000 | 2019-03-20 11:21:31 |
+----+-----------+---------+---------------------+
1 row in set (0.00 sec)
SessionB再次讀取id爲1的數據,balance拿到的還是1000,並沒有讀取SessionA提交的500,因爲MySql用MVCC對數據做了版本控制,解決了“不可重複讀”問題,是可以重複讀的,並且前後讀取的數據是一樣,所以,這個事務隔離級別稱爲“可重複讀”。
  mysql> update account set user_name = 'ZhangSan01' where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from account where id = 1;
+----+------------+---------+---------------------+
| id | user_name  | balance | create_time         |
+----+------------+---------+---------------------+
|  1 | ZhangSan01 |     500 | 2019-03-20 11:21:31 |
+----+------------+---------+---------------------+
1 row in set (0.00 sec)
SessionB將id爲1的user_name更新爲"ZhangSan01",然後再查下id爲1的數據,發現balance變爲了500,這是爲什麼,不是說好的MVCC版本控制的嗎?
這是因爲MySql在發生寫操作的時候,會讓數據同步爲最新的數據,所以SessionB在發生寫後讀取的就是數據庫持久化的最新數據。
SessionB在沒有對id爲1進行寫操作的時候,
每次讀取1000,但是當發生寫操作後,讀取數據庫持久化的最新值500,這個就是”幻讀“問題,彷彿給你變魔術變出來的一樣。

可串行化(Serializable)

爲了解決“幻讀”,將事務隔離級別提高到“可串行化”,下面演示“可串行化”事務隔離級別是怎麼回事,

以及會帶來什麼問題。

SessionA SessionB 說明
mysql>commit;
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='serializable';
Query OK, 0 rows affected (0.00 sec)
mysql>commit;
Query OK, 0 rows affected (0.00 sec)
mysql> set tx_isolation='serializable';
Query OK, 0 rows affected (0.00 sec)
在設置事務級別前,先commit一下,
預防出現你在幹別的,影響效果。
SessionA和SessionB事務隔離級別
設置爲可串行化(Serializable)。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> set Innodb_lock_wait_timeout = 10;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> set Innodb_lock_wait_timeout = 10;
Query OK, 0 rows affected (0.00 sec)
SessionA和SessionB開啓事務,並且設置鎖
會話鎖超時爲10秒。
mysql> select * from account;
+----+------------+---------+---------------------+
| id | user_name  | balance | create_time         |
+----+------------+---------+---------------------+
|  1 | ZhangSan01 |     500 | 2019-03-20 11:21:31 |
|  2 | LiSi       |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu     |    2500 | 2019-03-20 11:21:31 |
+----+------------+---------+---------------------+
3 rows in set (0.00 sec)
mysql> select * from account;
+----+------------+---------+---------------------+
| id | user_name  | balance | create_time         |
+----+------------+---------+---------------------+
|  1 | ZhangSan01 |     500 | 2019-03-20 11:21:31 |
|  2 | LiSi       |    3600 | 2019-03-20 11:21:31 |
|  3 | WangWu     |    2500 | 2019-03-20 11:21:31 |
+----+------------+---------+---------------------+
3 rows in set (0.00 sec)
SessionA和SessionB看到的數據一樣
  mysql> insert into account values(4, 'four', 400, now());
1205 - Lock wait timeout exceeded; try restarting transaction
SessionB執行insert阻塞,獲取不到鎖,被SessionA鎖住了。等10秒後,SessionB獲取鎖超時。
所以,可串行化(Serializable)隔離級別排隊上鎖執行,效率非常低,容易出現鎖超時等問題,實際中一般不用。任務排隊執行,沒有併發訪問數據情況,所以不會出現”幻讀“問題。

四 總結

InnoDB存儲引擎實現了行級鎖,在鎖定機制方面所帶來的性能損耗可能比表級鎖定更高一些,

但是在整體併發處理能力要遠遠優於MYISAM的表級鎖定的。當系統併發量高的時候,

InnoDB的整體性能和MYISAM相比就會有比較明顯的優勢了。但是,InnoDB的行級鎖定同樣也有其脆弱的一面,

當我們使用不當的時候,可能會讓Innodb的整體性能表現不僅不能比MYISAM高,甚至可能會更差。

但是,一般現實中很多業務都是需要事務的,所以一般都是用InnoDB引擎。

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