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

MySQL 事務和索引

 事務

什麼是事務

  • 事務就是將一組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.使用事務時應先關閉自動提交

-- 開始一個事務,標記事務的起始點
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條資料
*/

SET autocommit = 0 -- 關閉自動提交
-- 事務開啟
START TRANSACTION -- 標記一個事務的開啟,從這個之後的sql都在同一個事務內
-- 轉賬
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci
USE shopCREATE TABLE `account`(
`id` INT(3) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL,
`money` DECIMAL(9,2) NOT NULL,
PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8INSERT
INTO account(`name`,`money`)VALUES ('A',2000.00),('B',10000.00)-- 模擬轉賬
SET autocommit = 0; -- 關閉自動提交
START TRANSACTION -- 開啟一個事務
UPDATE account SET money=money-500 WHERE `name` = 'A' -- A減500
UPDATE account SET money=money+500 WHERE `name` = 'B' -- B加500
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 版本、儲存引擎和資料型別是否支援全文索引。
*/

拓展:測試索引

建表app_user:

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