1. 程式人生 > 實用技巧 >MySQL的事務和索引

MySQL的事務和索引

目錄

MySQL的事務-TCL(Transaction)

什麼是事務

一個事務是一個完整的業務邏輯單元,不可再分。

比如:銀行賬戶轉賬,從A賬戶向B賬戶轉賬10000,需要執行兩條update語句:
	update t_act set balance = balance - 10000 where actno = 'act-001';
	update t_act set balance = balance + 10000 where actno = 'act-001';
	以上兩條DML語句必須同時成功,或者同時失敗,不允許出現一條成功,一條失敗。
	要想保證以上的兩條DML語句同時成功或者同時失敗,那麼就需要使用資料庫的“事務機制”。
和事務相關的語句只有:DML語句。(insert delete update)
	為什麼?因為它們這三個語句都是和資料庫表當中的“資料”相關的。
	事務的存在是為了保證資料的完整性,安全性。

假設所有的業務都能使用1條DML語句搞定,還需要事務機制嗎?
	不需要事務。
	但實際情況不是這樣的。通常一個事務(業務)需要更多DML語句共同聯合完成。

事物的特性

事務包括四大特性:ACID
	A:原子性:事務是最小的工作單元,不可再分。
	C:一致性:事務必須保證多條DML語句同時成功或者同時失敗。
	I:隔離性:事務A與事務B之間具有隔離。
	D:永續性:永續性說的是最終資料必須持久化到硬碟檔案中,事務才算成功的結束。

事務之間的隔離性

事務隔離性存在隔離級別,理論上隔離級別包括4個:
	第一級別:讀未提交(read uncommitted)
		對方事務還沒有提交,我們當前事務可以讀取到對方未提交的資料。
		對方提交存在髒讀(Dirty Read)現象:表示堵到了髒的資料。
		
	第二級別:讀已提交(read committed)
		對方事務提交之後的資料我方可以讀取到。
		這種隔離級別解決了髒讀現象。
		讀已提交存在的問題是:不可重複讀。
		
	第三級別:可重複讀(repeatable read)
		這種隔離級別解決了不可重複讀問題。
		這種隔離級別存在的問題是:讀取到的資料是幻想。
		
	第四級別:序列化讀/序列化讀
		解決了所有問題
		效率低,需要事務排隊。
		
Oracle資料預設的隔離級別是:讀已提交。
MySQL資料庫預設的隔離級別是:可重複讀。

驗證隔離級別

設定事務的全域性隔離級別:
	set global transaction isolation level 隔離級別;
檢視事務的全域性隔離級別:
	select @@global.tx_isolation;
現表內資料:
mysql> select * from person;
+----+----------+
| id | name     |
+----+----------+
|  1 | ZHANGSAN |
|  2 | LISI     |
|  3 | WANGWU   |
+----+----------+
3 rows in set (0.00 sec)

讀未提交-read uncommitted

需要先設定隔離級別為read uncommitted(讀未提交)set global transaction isolation level read uncommitted 設定完成後需要exit退出一下再進入執行一下命令

讀已提交-read committed

序列化-serializable

使用者A
mysql> select @@global.tx_isolation;	//第一步:檢視到當前隔離級別
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| SERIALIZABLE          |
+-----------------------+
1 row in set (0.00 sec)

mysql> use test;
Database changed
mysql> select * from person;	//第二步:查詢表內容
+----+----------+
| id | name     |
+----+----------+
|  1 | ZHANGSAN |
|  2 | LISI     |
|  3 | WANGWU   |
+----+----------+
3 rows in set (0.00 sec)

mysql> start transaction;	//第三步:關閉自動提交機制
Query OK, 0 rows affected (0.00 sec)

mysql> select * from person;	//第六步:當用戶在person表內插入了新的內容,查詢person表全部資訊會發現游標在閃爍,一直等到使用者commit後才會查詢到	+----+----------+						person表的全部內容
| id | name     |
+----+----------+
|  1 | ZHANGSAN |
|  2 | LISI     |
|  3 | WANGWU   |
|  9 | XIAOHUA  |
+----+----------+
4 rows in set (10.66 sec)

使用者B
mysql> use test;
Database changed
mysql> start transaction;	//第四步:關閉自動提交機制
Query OK, 0 rows affected (0.00 sec)

mysql> insert into person(name) values('XIAOHUA'); //第五步:插入新內容
Query OK, 1 row affected (0.00 sec)

mysql> commit;	//第七步:提交
Query OK, 0 rows affected (0.01 sec)

可重複讀-repeatable read

演示事務

  • start transaction 關閉自動提交機制 ,
  • rollback 回滾機制
  • commit 提交機制
mysql事務預設情況下是自動提交的。(什麼是自動提交?只要執行任意一條DML語句則提交一次。)怎麼關閉自動提交? start transaction;

準備表:
	drop table if exists t_user;
	create table t_user(
        id int primary key auto_increment,
        username varchar(255)
    );
演示:當沒有使用 回滾和提交機制
	mysql> insert into t_user(username) values('zhangsan');
    Query OK, 1 row affected (0.01 sec)

    mysql> select * from t_user;
    +----+----------+
    | id | username |
    +----+----------+
    |  1 | zhangsan |
    +----+----------+
    1 row in set (0.00 sec)

    mysql> rollback;				//回滾後資料沒有發生改變
    Query OK, 0 rows affected (0.00 sec)

    mysql> select * from t_user;
    +----+----------+
    | id | username |
    +----+----------+
    |  1 | zhangsan |
    +----+----------+
    1 row in set (0.00 sec)
    
演示:使用 start transaction; 關閉自動提交機制.
    mysql> start transaction;
    Query OK, 0 rows affected (0.00 sec)

    mysql> insert into t_user(username) values('lisi');
    Query OK, 1 row affected (0.00 sec)

    mysql> select * from t_user;
    +----+----------+
    | id | username |
    +----+----------+
    |  1 | zhangsan |
    |  2 | lisi     |
    +----+----------+
    2 rows in set (0.00 sec)

    mysql> rollback;	//因為關閉了自動提交機制,所以回滾後發現剛才插入的資料刪除了。
    Query OK, 0 rows affected (0.01 sec)

    mysql> select * from t_user;
    +----+----------+
    | id | username |
    +----+----------+
    |  1 | zhangsan |
    +----+----------+
    1 row in set (0.00 sec)
	
演示:使用 commit; 提交機制
	mysql> start transaction;
	Query OK, 0 rows affected (0.00 sec)
	
	mysql> insert into t_user(username) values('jack'),('rose');
	Query OK, 2 rows affected (0.00 sec)
	Records: 2  Duplicates: 0  Warnings: 0
	
	mysql> select * from t_user;
    +----+----------+
    | id | username |
    +----+----------+
    |  1 | zhangsan |
    |  3 | jack     |
    |  4 | rose     |
    +----+----------+
    3 rows in set (0.00 sec)
	
	mysql> commit;
    Query OK, 0 rows affected (0.01 sec)

    mysql> rollback;	//當commit提交過後 就會發現回滾資料沒有發生改變
    Query OK, 0 rows affected (0.00 sec)

    mysql> select * from t_user;
    +----+----------+
    | id | username |
    +----+----------+
    |  1 | zhangsan |
    |  3 | jack     |
    |  4 | rose     |
    +----+----------+
    3 rows in set (0.00 sec)

索引

什麼是索引?有什麼用?

索引就相當於一本書的目錄,通過目錄可以快速的找到對應的資源。
在資料庫方面,查詢一張表的時候有兩種檢索方式:
	第一種方式:全表掃描
	第二種方式:根據索引檢索(效率很高)
	
索引為什麼可以提高檢索效率呢?
	其實最根本的原理是縮小了掃描的範圍。
索引雖然可以提高檢索效率,但是不能隨意的新增索引,因為索引也是資料庫當中的物件,也需要資料庫不斷的維護。是有維護成本的。比如:表中的資料經常被修改這樣就不適合新增索引,因為資料一旦修改,索引需要重新排列,進行維護。

如何建立和刪除索引?

建立索引物件:
	create index 索引名稱 on 表名(欄位名;
刪除索引物件:
	drop index 索引名稱 on 表名;

什麼時候給欄位新增索引?

資料量龐大(根據客戶的要求,根據線上的環境)
該欄位很少的DML操作(因為欄位進行修改操作,索引也需要維護)
該欄位經常出現在where子句中(經常根據哪個欄位查詢)

注意:主鍵和具有unique約束的欄位自動回新增索引。
根據主鍵查詢效率較高,儘量根據主鍵檢索。

檢視sql語句的執行計劃

mysql> explain select ename,sal from emp where sal=5000;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL |   14 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+

給薪資sal欄位新增索引:
mysql> create index emp_sal_index on emp(sal);
mysql> explain select ename,sal from emp where sal=5000;  	//	查詢後會發現檢索方式不是ALL而是ref了 檢索條目rows不是14而是1了
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key           | key_len | ref   | rows | Extra       |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+
|  1 | SIMPLE      | emp   | ref  | emp_sal_index | emp_sal_index | 9       | const |    1 | Using where |
+----+-------------+-------+------+---------------+---------------+---------+-------+------+-------------+

索引的實現原理

索引的底層採用的資料結構是:B + Tree

通過B Tree縮小掃描範圍,底層索引進行了排序、分割槽,索引回攜帶資料在表中的“實體地址”,最終通過索引檢索到資料之後,獲取到關聯的實體地址,通過實體地址定位表中的資料,效率是最高的。
	select ename from emp where ename = 'SMITH';
	通過索引轉換為:
		select ename from emp where 實體地址 = 0x123;

索引的分類

單一索引:給單個欄位新增索引
複合索引:給多個欄位聯合新增1個索引
主鍵索引:主鍵上會自動新增索引
唯一索引:有unique約束的欄位上會自動新增索引

索引什麼時候會失效?

select ename from emp where ename like '%A%';
模糊查詢的時候,第一個萬用字元使用的是%,這個時候索引是失效的。