1. 程式人生 > 其它 >MySQL(七)事務和索引

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 等索引;