1. 程式人生 > 實用技巧 >mysql6事務與索引

mysql6事務與索引

一,事務

1.什麼是事務

要麼成功,要麼失敗

事務是保證多個SQL操作的一致性,如果一條失敗全部SQL也將失效。


1.sql執行 A給B轉賬 A 1000 ---->200 B 200

2.sql執行 B收到A的錢 A 800 ------->B 400


  • 將一組sql放在同一個批次中去執行~如果一條失敗全部SQL也將失效。
  • 實際業務中大多數是對多個表操作,比如當發表文章時需要將文章的基本資訊發到文章基礎表和文章內容新增到文章內容表,這種情況不使用事務也沒有關係,如果出現數據異常重新新增就可以了
  • 但牽涉到貨幣的情況就必須使用事務了,必須保證貨幣處理是準確的
  • 當然有些公司要求所有查詢都使用事務,這就遵照公司要求完成就可以了

事務原則:ACID原則 原子性,一致性,隔離性,永續性 (髒讀,幻讀)

參考部落格連線:https://blog.csdn.net/u010651249/article/details/83863147

原子性(Atomicity)

要麼都成功,要麼都失敗

一致性(Consistency)

事務前後的資料必須保證一致,1000

永續性(Durability)--事務提交

事務一旦提交不可逆,被持久化到資料庫中!

隔離性(Isolation)

事務的隔離性是多個使用者併發訪問資料庫,資料庫為每一個使用者開啟事務,不能被其他操作資料所幹擾事務之間要互相隔離。

隔離所導致的問題

髒讀:

指一個事務讀取了另外一個事務未提交的資料。

不可重複讀:

在一個事務內讀取表中的某一行資料,多次讀取結果不同。(這個不一定是錯誤,只是某些場合不對)

虛讀(幻讀)

是指在一個事務內讀取到了別的事務插入的資料,導致前後讀取不一致。
(一般是行影響,多了一行)

執行事務

--===========事務=============--
--mysql是預設開啟事務自動提交的
SET autocommit=0 /*關閉*/
SET autocommit=1 /*開啟(預設的)*/

--手動開啟事務
SET autocommit=0 --關閉自動提交
--事務開啟
START TRANSACTION --標記一個事務的開啟

INSERT xxx
INSERT xxx
--提交:持久化(成功!)
COMMIT
--回滾:回到的原來的樣子(失敗!)
ROLLBACK 
--事務結束
SET autocommit=1 --開啟自動提交

--瞭解
SAVEPOINT 儲存點名 --設定一個事務的儲存點
ROLLBACK TO SAVEPOINT --儲存點名 --回滾到儲存點
RELEASE SAVEPOINT --撤銷儲存點

執行事務案例

--轉賬
CREATE DATABASE `shop` CHARACTER SET utf8 COLLATE utf8_general_ci
USE shop

CREATE TABLE `accout`(
    `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=utf8

--模擬轉賬:事務
SET autocommit = 0;--關閉自動提交
START TRANSACTION --開啟一個事務(一組事務)
INSERT INTO `accout`(`name`,`money`)VALUES('A',20000.00),('B',10000.00)
UPDATE `accout` set `money`=`money`-500 WHERE `name`='A' ;
UPDATE `accout` set `money`=`money`+500 WHERE `name`='B'

COMMIT; --提交事務被持久化了!
ROLLBACK; --回滾


SET autocommit=1;--恢復預設值
 

事務隔離級別:

有四種隔離級別,分別是讀未提交(Read uncommitted),讀已提交(Read committed),可重複讀(Repeatable read),可序列化(Serializable),用來解決資料庫操作中產生的各種問題。

一、讀未提交(Read uncommitted)

在這種隔離級別下,所有事務能夠讀取其他事務未提交的資料。讀取其他事務未提交的資料,會造成髒讀。因此在該種隔離級別下,不能解決髒讀、不可重複讀和幻讀。

讀未提交可能會產生髒讀的現象,那麼怎麼解決髒讀呢?那就是使用讀已提交

二、讀已提交(Read committed)

在這種隔離級別下,所有事務只能讀取其他事務已經提交的內容。能夠徹底解決髒讀的現象。但在這種隔離級別下,會出現一個事務的前後多次的查詢中卻返回了不同內容的資料的現象,也就是出現了不可重複讀。

已提交可能會產生不可重複讀的現象,我們可以使用可重複讀。

三、可重複讀(Repeatable read)

在這種隔離級別下,所有事務前後多次的讀取到的資料內容是不變的。也就是某個事務在執行的過程中,不允許其他事務進行update操作,但允許其他事務進行add操作,造成某個事務前後多次讀取到的資料總量不一致的現象,從而產生幻讀。

注意【1】這才是mysql的預設事務隔離級別

可重複讀依然會產生幻讀的現象,此時我們可以使用序列化來解決。

四、可序列化(Serializable)

在這種隔離級別下,所有的事務順序執行,所以他們之間不存在衝突,從而能有效地解決髒讀、不可重複讀和幻讀的現象。但是安全和效率不能兼得,這樣事務隔離級別,會導致大量的操作超時和鎖競爭,從而大大降低資料庫的效能,一般不使用這樣事務隔離級別。

下面用一張表格來表示他們能夠解決的問題
隔離級別 髒讀 不可重複讀 幻讀
讀未提交(Read uncommitted) ×(未解決) × ×
讀已提交(Read committed) √(解決) × ×
可重複讀(Repeatable read) ×
可序列化(Serializable)

二,索引

Mysql官方對索引的定義為:索引(Index)是幫助Mysql快速高效獲取資料的結構,提取句子骨幹,就可以得到索引的本質:索引是資料結構

1.索引的分類

在一個表中,主鍵索引只能有一個,唯一索引可以有多個

  • 主鍵索引(PRIMARY KEY)

唯一的標識,主鍵不可以重複,只能有一個列作為主鍵

  • 唯一索引(UNIQUE KEY)

避免重複的列出現,唯一索引可以重複,多個列都可以標識 為唯一索引

  • 常規索引(KEY/INDEX)

預設的,index或者key關鍵字來設定

  • 全文索引(FullText)

在指定的資料庫下的引擎下有,MyISAM,快速定位資料---現在 都有了

語法格式:

 
 ----============索引============--
 --索引的使用
 --1.在建表的時候給欄位新增索引
 --2.建立完畢新增索引
 
 
 --1.顯示錶所有的索引資訊
 SHOW INDEX FROM student
 
 --新增一個全文索引   索引名稱(類名)
 ALTER  TABLE `student` ADD FULLTEXT INDEX `StudentName`(`StudentName`)
 
 --EXPLAIN 分析sql的執行情況
 EXPLAIN SELECT * FROM student
 
 EXPLAIN SELECT * FROM  student WHERE MATCH(StudentName) AGAINST('劉')

2.測試索引

建立的app_user表中 UNSIGNED 表示該欄位插入資料不能為負數

CREATE TABLE `app_user`(
        --UNSIGNED不能為負數
	`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 COMMENT '註冊時間',
	`update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間',
	PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app使用者表';

--插入100萬的資料

-- 插入100萬資料
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();
-- ----------------------------------------------------------------------------------
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)
)

TRUNCATE `app_user`

DROP FUNCTION mock_data

-- ----------------------------------------------------------------------------------
-- 執行耗時   : 0.525 
-- sec傳送時間   : 0 sec
-- 總耗時      : 0.526 sec
SELECT * FROM `app_user` WHERE `name`='使用者9999';
EXPLAIN SELECT * FROM `app_user` WHERE `name`='使用者9999';



-- ALTER TABLE `student` ADD FULLTEXT INDEX `StudentName`(`StudentName`);

-- id_表名_欄位名
-- CREATE INDEX 索引名 ON 表名(欄位名);
CREATE INDEX id_app_user_name ON app_user(`name`);
-- 執行耗時   : 0 sec
-- 傳送時間   : 0 sec
-- 總耗時      : 0.001 sec
SELECT * FROM `app_user` WHERE `name`='使用者9999';
EXPLAIN SELECT * FROM `app_user` WHERE `name`='使用者9999';

給表字段新增索引語法:

1.新增一個全文索引 索引名稱(類名)

-- ALTER TABLE student ADD FULLTEXT INDEX StudentName(StudentName);

2.-- id_表名_欄位名
-- CREATE INDEX 索引名 ON 表名(欄位名);

CREATE INDEX id_app_user_name ON app_user(name);

索引在小資料的時候,用處不大,但是在大資料的時候,區別十分明顯!

3.索引原則

  • 索引不是越多越好
  • 不要對程序變動資料加索引
  • 小資料的表不需要加索引
  • 索引一般加在查詢欄位上!

​ 索引的設計可以遵循一些已有的原則,建立索引的時候請儘量考慮符合這些原則,便於提升索引的使用效率,更高效的使用索引。

  • 對查詢頻次較高,且資料量比較大的表建立索引。
  • 索引欄位的選擇,最佳候選列應當從where子句的條件中提取,如果where子句中的組合比較多,那麼應當挑選最常用、過濾效果最好的列的組合。
  • 使用唯一索引,區分度越高,使用索引的效率越高。
  • 索引可以有效的提升查詢資料的效率,但索引數量不是多多益善,索引越多,維護索引的代價自然也就水漲船高。對於插入、更新、刪除等DML操作比較頻繁的表來說,索引過多,會引入相當高的維護代價,降低DML操作的效率,增加相應操作的時間消耗。另外索引過多的話,MySQL也會犯選擇困難病,雖然最終仍然會找到一個可用的索引,但無疑提高了選擇的代價。
  • 使用短索引,索引建立之後也是使用硬碟來儲存的,因此提升索引訪問的I/O效率,也可以提升總體的訪問效率。假如構成索引的欄位總長度比較短,那麼在給定大小的儲存塊內可以儲存更多的索引值,相應的可以有效的提升MySQL訪問索引的I/O效率。
  • 利用最左字首,N個列組合而成的組合索引,那麼相當於是建立了N個索引,如果查詢時where子句中使用了組成該索引的前幾個欄位,那麼這條查詢SQL可以利用組合索引來提升查詢效率。
建立複合索引:

	CREATE INDEX idx_name_email_status ON tb_seller(NAME,email,STATUS);

就相當於
	對name 建立索引 ;
	對name , email 建立了索引 ;
	對name , email, status 建立了索引 ;

索引的資料結構

Hash:型別的索引

Btree:InnoDB的預設的資料結構

閱讀:http://blog.codinglabs.org/articles/theory-of-mysql-index.html