MySQL(七)事務和索引
事務和索引
事務(Transaction)
要麼都成功,要麼都失敗
什麼是事務
- 事務就是將一組SQL語句放在同一批次內去執行
- 如果一個SQL語句出錯,則該批次內的所有SQL都將被取消執行
- MySQL事務處理只支援InnoDB和BDB資料表型別
事務的ACID原則
隔離級別參考:事務的四個特性及隔離級別
- 原子性(Atomic)
整個事務中的所有操作,要麼全部完成,要麼全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被回滾(ROLLBACK)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
- 一致性(Consist)
一個事務可以封裝狀態改變(除非它是一個只讀的)。事務必須始終保持系統處於一致的狀態,不管在任何給定的時間併發事務有多少。也就是說:如果事務是併發多個,系統也必須如同序列事務一樣操作。其主要特徵是保護性和不變性(Preserving an Invariant),以轉賬案例為例,假設有五個賬戶,每個賬戶餘額是100元,那麼五個賬戶總額是500元,如果在這個5個賬戶之間同時發生多個轉賬,無論併發多少個,比如在A與B賬戶之間轉賬5元,在C與D賬戶之間轉賬10元,在B與E之間轉賬15元,五個賬戶總額也應該還是500元,這就是保護性和不變性。
- 隔離性(Isolated)
隔離狀態執行事務,使它們好像是系統在給定時間內執行的唯一操作。如果有兩個事務,執行在相同的時間內,執行相同的功能,事務的隔離性將確保每一事務在系統中認為只有該事務在使用系統。這種屬性有時稱為序列化,為了防止事務操作間的混淆,必須序列化或序列化請求,使得在同一時間僅有一個請求用於同一資料。
- 永續性(Durable)
在事務完成以後,該事務對資料庫所作的更改便持久的儲存在資料庫之中,並不會被回滾,不可逆性
執行事務步驟
-- 使用set語句來改變自動提交模式 SET autocommit = 0; /*關閉*/ SET autocommit = 1; /*開啟*/ -- 注意: -- 1.MySQL中預設是自動提交 -- 2.使用事務時應先關閉自動提交 -- 開始一個事務,標記事務的起始點 -- 從這個之後所有的sql都在同一個事務內 START TRANSACTION -- 提交一個事務給資料庫(持久化) COMMIT -- 將事務回滾,資料回到本次事務的初始狀態 ROLLBACK -- 事務結束 -- 還原MySQL資料庫的自動提交 SET autocommit =1; -- 儲存點 SAVEPOINT +儲存點名稱 -- 設定一個事務儲存點 ROLLBACK TO SAVEPOINT +儲存點名稱 -- 回滾到儲存點 RELEASE SAVEPOINT +儲存點名稱 -- 刪除儲存點
模擬場景轉賬
/* A線上買一款價格為500元商品,網上銀行轉賬. A的銀行卡餘額為2000,然後給商家B支付500. 商家B一開始的銀行卡餘額為10000 建立資料庫shop和建立表account並插入2條資料 */ CREATE DATABASE `shop`CHARACTER SET utf8 COLLATE utf8_general_ci; USE `shop`; CREATE TABLE `account` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(32) NOT NULL, `cash` DECIMAL(9,2) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO account (`name`,`cash`) VALUES('A',2000.00),('B',10000.00) -- 轉賬實現 SET autocommit = 0; -- 關閉自動提交 START TRANSACTION; -- 開始一個事務,標記事務的起始點 UPDATE account SET cash=cash-500 WHERE `name`='A'; UPDATE account SET cash=cash+500 WHERE `name`='B'; COMMIT; -- 提交事務 # rollback; SET autocommit = 1; -- 恢復自動提交
索引
索引的作用
- 提高查詢速度
- 確保資料的唯一性
- 可以加速表和表之間的連線 , 實現表與表之間的參照完整性
- 使用分組和排序子句進行資料檢索時 , 可以顯著減少分組和排序的時間
- 全文檢索欄位進行搜尋優化.
分類
- 主鍵索引 (Primary Key)
- 唯一索引 (Unique)
- 常規索引 (Index)
- 全文索引 (FullText)
主鍵索引
主鍵 : 某一個屬性組能唯一標識一條記錄
特點 :
- 最常見的索引型別
- 確保資料記錄的唯一性
- 確定特定資料記錄在資料庫中的位置
唯一索引
作用 : 避免同一個表中某資料列中的值重複
與主鍵索引的區別
- 主鍵索引只能有一個
- 唯一索引可能有多個
CREATE TABLE `Grade`(
`GradeID` INT(11) AUTO_INCREMENT PRIMARYKEY,
`GradeName` VARCHAR(32) NOT NULL UNIQUE
-- 或 UNIQUE KEY `GradeID` (`GradeID`)
)
常規索引
作用 : 快速定位特定資料
注意 :
- index 和 key 關鍵字都可以設定常規索引
- 應加在查詢找條件的欄位
- 不宜新增太多常規索引,影響資料的插入,刪除和修改操作
CREATE TABLE `result`(
-- 省略一些程式碼
INDEX/KEY `ind` (`studentNo`,`subjectNo`) -- 建立表時新增
)
-- 建立後新增
ALTER TABLE `result` ADD INDEX `ind`(`studentNo`,`subjectNo`);
全文索引
百度搜索:全文索引
作用 : 快速定位特定資料
注意 :
- 只能用於MyISAM型別的資料表
- 只能用於CHAR , VARCHAR , TEXT資料列型別
- 適合大型資料集
/*
#方法一:建立表時
CREATE TABLE 表名 (
欄位名1 資料型別 [完整性約束條件…],
欄位名2 資料型別 [完整性約束條件…],
[UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY
[索引名] (欄位名[(長度)] [ASC |DESC])
);
#方法二:CREATE在已存在的表上建立索引
CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名
ON 表名 (欄位名[(長度)] [ASC |DESC]) ;
#方法三:ALTER TABLE在已存在的表上建立索引
ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX
索引名 (欄位名[(長度)] [ASC |DESC]) ;
#刪除索引:DROP INDEX 索引名 ON 表名字;
#刪除主鍵索引: ALTER TABLE 表名 DROP PRIMARY KEY;
#顯示索引資訊: SHOW INDEX FROM student;
*/
/*增加全文索引*/
ALTER TABLE `school`.`student` ADD FULLTEXT INDEX `studentname` (`StudentName`);
/*EXPLAIN : 分析SQL語句執行效能*/
EXPLAIN SELECT * FROM student WHERE studentno='1000';
/*使用全文索引*/
-- 全文搜尋通過 MATCH() 函式完成。
-- 搜尋字串作為 against() 的引數被給定。搜尋以忽略字母大小寫的方式執行。對於表中的每個記錄行,MATCH() 返回一個相關性值。即,在搜尋字串與記錄行在 MATCH() 列表中指定的列的文字之間的相似性尺度。
EXPLAIN SELECT *FROM student WHERE MATCH(studentname) AGAINST('love');
/*
開始之前,先說一下全文索引的版本、儲存引擎、資料型別的支援情況
MySQL 5.6 以前的版本,只有 MyISAM 儲存引擎支援全文索引;
MySQL 5.6 及以後的版本,MyISAM 和 InnoDB 儲存引擎均支援全文索引;
只有欄位的資料型別為 char、varchar、text 及其系列才可以建全文索引。
測試或使用全文索引時,要先看一下自己的 MySQL 版本、儲存引擎和資料型別是否支援全文索引。
*/
測試索引
建表
CREATE TABLE `app_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT '' COMMENT '使用者暱稱',
`email` varchar(50) NOT NULL COMMENT '使用者郵箱',
`phone` varchar(20) DEFAULT '' COMMENT '手機號',
`gender` tinyint(4) unsigned DEFAULT '0' COMMENT '性別(0:男;1:女)',
`password` varchar(100) NOT NULL COMMENT '密碼',
`age` tinyint(4) DEFAULT '0' COMMENT '年齡',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='app使用者表'
批量插入100w 條資料
DROP FUNCTION IF EXISTS mock_data;
DELIMITER $$
CREATE FUNCTION mock_data()
RETURNS INT
BEGIN
DECLARE num INT DEFAULT 1000000;
DECLARE i INT DEFAULT 0;
WHILE i < num DO
INSERT INTO app_user(`name`, `email`, `phone`, `gender`, `password`, `age`)
VALUES(CONCAT('使用者', i), '[email protected]', CONCAT('18', FLOOR(RAND()*(999999999-100000000)+100000000)),FLOOR(RAND()*2),UUID(), FLOOR(RAND()*100));
SET i = i + 1;
END WHILE;
RETURN i;
END;
SELECT mock_data();
索引效率測試
無索引
SELECT * FROM app_user WHERE name = '使用者9999'; -- 檢視耗時
SELECT * FROM app_user WHERE name = '使用者9999';
SELECT * FROM app_user WHERE name = '使用者9999';
mysql> EXPLAIN SELECT * FROM app_user WHERE name = '使用者9999'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: app_user
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 992759 -- 對比
filtered: 10.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
建立索引
CREATE INDEX idx_app_user_name ON app_user(name);
測試普通索引
mysql> EXPLAIN SELECT * FROM app_user WHERE name = '使用者9999'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: app_user
partitions: NULL
type: ref
possible_keys: idx_app_user_name
key: idx_app_user_name
key_len: 203
ref: const
rows: 1 -- 對比
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
mysql> SELECT * FROM app_user WHERE name = '使用者9999';
1 row in set (0.00 sec)
mysql> SELECT * FROM app_user WHERE name = '使用者9999';
1 row in set (0.00 sec)
mysql> SELECT * FROM app_user WHERE name = '使用者9999';
1 row in set (0.00 sec)
索引準則
- 索引不是越多越好
- 不要對經常變動的資料加索引
- 小資料量的表建議不要加索引
- 索引一般應加在查詢條件的欄位
索引資料結構
-- 我們可以在建立上述索引的時候,為其指定索引型別,分兩類
hash型別的索引:查詢單條快,範圍查詢慢
btree型別的索引:b+樹,層數越多,資料量指數級增長(我們就用它,因為innodb預設支援它)
-- 不同的儲存引擎支援的索引型別也不一樣
InnoDB 支援事務,支援行級別鎖定,支援 B-tree、Full-text 等索引,不支援 Hash 索引;
MyISAM 不支援事務,支援表級別鎖定,支援 B-tree、Full-text 等索引,不支援 Hash 索引;
Memory 不支援事務,支援表級別鎖定,支援 B-tree、Hash 等索引,不支援 Full-text 索引;
NDB 支援事務,支援行級別鎖定,支援 Hash 索引,不支援 B-tree、Full-text 等索引;
Archive 不支援事務,支援表級別鎖定,不支援 B-tree、Hash、Full-text 等索引;