MySQL 索引、事務與存儲引擎 MySQL 索引 1.索引的概念 ●索引是一個排序的列表,在這個列表中存儲著索引的值和包含這個值的數據所在行的物理地址(類似於C語言的鏈表通過指針指向數據記錄的記憶體地址)。 ●使用索引後可以不用掃描全表來定位某行的數據,而是先通過索引表找到該行數據對應的物理地址然後 ...
---MySQL 索引---
1.索引的概念
●索引是一個排序的列表,在這個列表中存儲著索引的值和包含這個值的數據所在行的物理地址(類似於C語言的鏈表通過指針指向數據記錄的記憶體地址)。 ●使用索引後可以不用掃描全表來定位某行的數據,而是先通過索引表找到該行數據對應的物理地址然後訪問相應的數據,因此能加快資料庫的查詢速度。 ●索引就好比是一本書的目錄,可以根據目錄中的頁碼快速找到所需的內容。 ●索引是表中一列或者若幹列值排序的方法。 ●建立索引的目的是加快對錶中記錄的查找或排序。
2.索引的作用
●設置了合適的索引之後,資料庫利用各種快速定位技術,能夠大大加快查詢速度,這是創建索引的最主要的原因。 ●當表很大或查詢涉及到多個表時,使用索引可以成千上萬倍地提高查詢速度。 ●可以降低資料庫的IO成本,並且索引還可以降低資料庫的排序成本。 ●通過創建唯一性索引,可以保證數據表中每一行數據的唯一性。 ●在使用分組和排序時,可大大減少分組和排序的時間。 ●建立索引在搜索和恢複數據庫中的數據時能顯著提高性能
索引的副作用:
●索引需要占用額外的磁碟空間。 對於 MyISAM 引擎而言,索引文件和數據文件是分離的,索引文件用於保存數據記錄的地址。 而 InnoDB 引擎的表數據文件本身就是索引文件。 ●更新一個包含索引的表需要比更新一個沒有索引的表花費更多的時間,這是由於索引本身也需要更新。因此,理想的做法是僅僅在常常被搜索的列(以及表)上面創建索引。
3.創建索引的原則依據
索引雖可以提升資料庫查詢的速度,但並不是任何情況下都適合創建索引。因為索引本身會消耗系統資源,在有索引的情況下,資料庫會先進行索引查詢,然後定位到具體的數據行,如果索引使用不當,反而會增加資料庫的負擔。 ●表的主鍵、外鍵必須有索引。因為主鍵具有唯一性,外鍵關聯的是主表的主鍵,查詢時可以快速定位。 ●記錄數超過300行的表應該有索引。如果沒有索引,每次查詢都需要把表遍歷一遍,會嚴重影響資料庫的性能。 ●經常與其他表進行連接的表,在連接欄位上應該建立索引。 ●唯一性太差的欄位不適合建立索引。 ●更新太頻繁地欄位不適合創建索引。 ●經常出現在 where 子句中的欄位,特別是大表的欄位,應該建立索引。 ●在經常進行 GROUP BY、ORDER BY 的欄位上建立索引; ●索引應該建在選擇性高的欄位上。 ●索引應該建在小欄位上,對於大的文本欄位甚至超長欄位,不要建索引。
4.索引的分類和創建
create table member (id int(10),name varchar(10),cardid int(18),phone int(11),address varchar(50),remark text);
普通索引
(1)普通索引:最基本的索引類型,沒有唯一性之類的限制。
●直接創建索引
CREATE INDEX 索引名 ON 表名 (列名[(length)]);
#(列名(length)):length是可選項,下同。如果忽略 length 的值,則使用整個列的值作為索引。如果指定,使用列的前 length 個字元來創建索引,這樣有利於減小索引文件的大小。在不損失精確性的情況下,長度越短越好。 #索引名建議以“_index”結尾。
-
length
是可選的參數,用於指定索引列的長度。這在某些資料庫系統中可能是必需的,以限制索引的大小。
create index name_index on member (name);
●修改表方式創建
ALTER TABLE 表名 ADD INDEX 索引名 (列名);
●創建表的時候指定索引
CREATE TABLE 表名 ( 欄位1 數據類型,欄位2 數據類型[,...],INDEX 索引名 (列名));
唯一索引
(2)唯一索引:與普通索引類似,但區別是唯一索引列的每個值都唯一。唯一索引允許有空值(註意和主鍵不同)。如果是用組合索引創建,則列值的組合必須唯一。添加唯一鍵將自動創建唯一索引。 ●直接創建唯一索引:
CREATE UNIQUE INDEX 索引名 ON 表名(列名);
create unique index cardid_index on member(cardid);
●修改表方式創建
ALTER TABLE 表名 ADD UNIQUE 索引名 (列名);
●創建表的時候指定
CREATE TABLE 表名 (欄位1 數據類型,欄位2 數據類型[,...],UNIQUE 索引名 (列名));
主鍵索引
(3)主鍵索引:是一種特殊的唯一索引,必須指定為“PRIMARY KEY”。一個表只能有一個主鍵,不允許有空值。 添加主鍵將自動創建主鍵索引。 ●創建表的時候指定
CREATE TABLE 表名 ([...],PRIMARY KEY (列名));
●修改表方式創建
ALTER TABLE 表名 ADD PRIMARY KEY (列名);
alter table member add primary key (id);
組合索引
(4)組合索引(單列索引與多列索引):可以是單列上創建的索引,也可以是在多列上創建的索引。需要滿足最左原則,因為 select 語句的 where 條件是依次從左往右執行的,所以在使用 select 語句查詢時 where 條件使用的欄位順序必須和組合索引中的排序一致,否則索引將不會生效。
CREATE TABLE 表名 (列名1 數據類型,列名2 數據類型,列名3 數據類型,INDEX 索引名 (列名1,列名2,列名3));
select * from 表名 where 列名1='...' AND 列名2='...' AND 列名3='...';
全文索引
(5)全文索引(FULLTEXT):適合在進行模糊查詢的時候使用,可用於在一篇文章中檢索文本信息。在 MySQL5.6 版本以前 FULLTEXT 索引僅可用於 MyISAM 引擎,在 5.6 版本之後 innodb 引擎也支持 FULLTEXT 索引。全文索引可以在 CHAR、VARCHAR 或者 TEXT 類型的列上創建。 ●直接創建索引
CREATE FULLTEXT INDEX 索引名 ON 表名 (列名);
●修改表方式創建
ALTER TABLE 表名 ADD FULLTEXT 索引名 (列名);
alter table member add fulltext remark_index (remark);
●創建表的時候指定索引
CREATE TABLE 表名 (欄位1 數據類型[,...],FULLTEXT 索引名 (列名));
#數據類型可以為 CHAR、VARCHAR 或者 TEXT
●使用全文索引查詢
SELECT * FROM 表名 WHERE MATCH(列名) AGAINST('查詢內容');
insert into member values(1,'zhangsan',123123,123123,'nanjing','this is member!');
insert into member values(2,'lisi',456456,456456,'beijing','this is vip!');
insert into member values(3,'wangwu',789789,78979,'shanghai','this is vip member!');
select * from member where match(remark) against('vip');
5.查看索引
show index from 表名;
show keys from 表名;
各欄位的含義如下: Table:表的名稱。 Non_unique:如果索引不能包括重覆詞,則為 0;如果可以,則為 1。 Key_name:索引的名稱。 Seq_in_index:索引中的列序號,從 1 開始。 Column_name:列名稱。 Collation:列以什麼方式存儲在索引中。在 MySQL 中,有值‘A’(升序)或 NULL(無分類)。 Cardinality:索引中唯一值數目的估計值。 Sub_part:如果列只是被部分地編入索引,則為被編入索引的字元的數目。如果整列被編入索引,則為 NULL。 Packed:指示關鍵字如何被壓縮。如果沒有被壓縮,則為 NULL。 Null:如果列含有 NULL,則含有 YES。如果沒有,則該列含有 NO。 Index_type:用過的索引方法(BTREE, FULLTEXT, HASH, RTREE)。 Comment:備註。
6.刪除索引
●直接刪除索引
DROP INDEX 索引名 ON 表名;
●修改表方式刪除索引
ALTER TABLE 表名 DROP INDEX 索引名;
●刪除主鍵索引
ALTER TABLE 表名 DROP PRIMARY KEY;
案例:比如為某商場做一個會員卡的系統。這個系統有一個會員表,有下列欄位: 會員編號 INT 會員姓名 VARCHAR(10) 會員身份證號碼 INT(18) 會員電話 INT(11) 會員住址 VARCHAR(50) 會員備註信息 TEXT
#創建會員表
create table member (id int(10),name varchar(10),cardid varchar(18),phone int(11),address varchar(50),remark text);
#將表id設為主鍵
alter table member add primary key (id);
#添加普通索引
create index name_index on member (name);
create unique index cardid_index on member(cardid);
alter table member add fulltext remark_index (remark);
那麼會員編號,作為主鍵,使用 PRIMARY KEY 會員姓名,如果要建索引的話,那麼就是普通的 INDEX 會員身份證號碼,如果要建索引的話,那麼可以選擇 UNIQUE (唯一的,不允許重覆) 會員備註信息,如果需要建索引的話,可以選擇 FULLTEXT,全文搜索。 不過 FULLTEXT 用於搜索很長一篇文章的時候,效果最好。用在比較短的文本,如果就一兩行字的,普通的 INDEX 也可以。
----MySQL 事務---
1.事務的概念
●事務是一種機制、一個操作序列,包含了一組資料庫操作命令,並且把所有的命令作為一個整體一起向系統提交或撤銷操作請求,即這一組資料庫命令要麼都執行,要麼都不執行。 ●事務是一個不可分割的工作邏輯單元,在資料庫系統上執行併發操作時,事務是最小的控制單元。 ●事務適用於多用戶同時操作的資料庫系統的場景,如銀行、保險公司及證券交易系統等等。 ●事務通過事務的整體性以保證數據的一致性。 ●事務能夠提高在向表中更新和插入信息期間的可靠性。
說白了,所謂事務,它是一個操作序列,這些操作要麼都執行,要麼都不執行,它是一個不可分割的工作單位。
2.事務的ACID特點
ACID,是指在可靠資料庫管理系統(DBMS)中,事務(transaction)應該具有的四個特性:原子性(Atomicity)**、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)**。這是可靠資料庫所應具備的幾個特性。
●原子性:指事務是一個不可再分割的工作單位,事務中的操作要麼都發生,要麼都不發生。 事務是一個完整的操作,事務的各元素是不可分的。 事務中的所有元素必須作為一個整體提交或回滾。 如果事務中的任何元素失敗,則整個事務將失敗。
案例: A給B轉帳100元錢的時候只執行了扣款語句,就提交了,此時如果突然斷電,A賬號已經發生了扣款,B賬號卻沒收到加款,在生活中就會引起糾紛。這種情況就需要事務的原子性來保證事務要麼都執行,要麼就都不執行。
●一致性:指在事務開始之前和事務結束以後,資料庫的完整性約束沒有被破壞。 當事務完成時,數據必須處於一致狀態。 在事務開始前,資料庫中存儲的數據處於一致狀態。 在正在進行的事務中,數據可能處於不一致的狀態。 當事務成功完成時,數據必須再次回到已知的一致狀態。
案例: 對銀行轉帳事務,不管事務成功還是失敗,應該保證事務結束後表中A和B的存款總額跟事務執行前一致。
●隔離性:指在併發環境中,當不同的事務同時操縱相同的數據時,每個事務都有各自的完整數據空間。 對數據進行修改的所有併發事務是彼此隔離的,表明事務必須是獨立的,它不應以任何方式依賴於或影響其他事務。 修改數據的事務可在另一個使用相同數據的事務開始之前訪問這些數據,或者在另一個使用相同數據的事務結束之後訪問這些數據。 也就是說併發訪問資料庫時,一個用戶的事務不被其他事務所干擾,各併發事務之間資料庫是獨立的。
//當多個客戶端併發地訪問同一個表時,可能出現下麵的一致性問題: (1)臟讀:當一個事務正在訪問數據,並且對數據進行了修改,而這種修改還沒有提交到資料庫中,這時,另外一個事務也訪問這個數據,然後使用了這個數據。 (2)不可重覆讀:指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那麼,在第一個事務中的兩次讀數據之間,由於第二個事務的修改,那麼第一個事務兩次讀到的的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱為是不可重覆讀。(即不能讀到相同的數據內容) (3)幻讀:一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,另一個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,操作前一個事務的用戶會發現表中還有一個沒有修改的數據行,就好象發生了幻覺一樣。 (4)丟失更新:兩個事務同時讀取同一條記錄,A先修改記錄,B也修改記錄(B不知道A修改過),B提交數據後B的修改結果覆蓋了A的修改結果。
//事務的隔離級別決定了事務之間可見的級別。 MySQL事務支持如下四種隔離,用以控制事務所做的修改,並將修改通告至其它併發的事務: (1)未提交讀(Read Uncommitted(RU)): 允許臟讀,即允許一個事務可以看到其他事務未提交的修改。
(2)提交讀(Read Committed(RC)): 允許一個事務只能看到其他事務已經提交的修改,未提交的修改是不可見的。防止臟讀。
(3)可重覆讀(Repeatable Read(RR)):---mysql預設的隔離級別 確保如果在一個事務中執行兩次相同的SELECT語句,都能得到相同的結果,不管其他事務是否提交這些修改。可以防止臟讀和不可重覆讀。
(4)串列讀(Serializable):---相當於鎖表 完全串列化的讀,將一個事務與其他事務完全地隔離。每次讀都需要獲得表級共用鎖,讀寫相互都會阻塞。可以防止臟讀,不可重覆讀取和幻讀,(事務串列化)會降低資料庫的效率。
mysql預設的事務處理級別是 repeatable read ,而Oracle和SQL Server是 read committed 。
//事務隔離級別的作用範圍分為兩種: ● 全局級:對所有的會話有效 ● 會話級:只對當前的會話有效
查詢全局事務隔離級別:
show global variables like '%isolation%';
SELECT @@global.tx_isolation;
查詢會話事務隔離級別:
show session variables like '%isolation%';
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;
設置全局事務隔離級別:
set global transaction isolation level read committed;
set @@global.tx_isolation='read-committed'; #重啟服務後失效
設置會話事務隔離級別:
set session transaction isolation level repeatable read;
set @@session.tx_isolation='repeatable-read';
●持久性:在事務完成以後,該事務所對資料庫所作的更改便持久的保存在資料庫之中,並不會被回滾。 指不管系統是否發生故障,事務處理的結果都是永久的。 一旦事務被提交,事務的效果會被永久地保留在資料庫中。
總結:在事務管理中,原子性是基礎,隔離性是手段,一致性是目的,持久性是結果。
3.事務控制語句
BEGIN 或 START TRANSACTION:顯式地開啟一個事務。
COMMIT 或 COMMIT WORK:提交事務,並使已對資料庫進行的所有修改變為永久性的。
ROLLBACK 或 ROLLBACK WORK:回滾會結束用戶的事務,並撤銷正在進行的所有未提交的修改。
SAVEPOINT S1:使用 SAVEPOINT 允許在事務中創建一個回滾點,一個事務中可以有多個 SAVEPOINT;“S1”代表回滾點名稱。
ROLLBACK TO [SAVEPOINT] S1:把事務回滾到標記點。
案例:
use gy;
create table account(
id int(10) primary key not null,
name varchar(40),
money double
);
insert into account values(1,'A',1000);
insert into account values(2,'B',1000);
#測試提交事務
begin; #開啟事務
update account set money= money - 100 where name='A';
commit;#提交事務
quit
mysql -u root -p
use gy;
select * from account;
#測試回滾事務
begin;
update account set money= money + 100 where name='A';
rollback;
mysql -u root -p
use gy;
select * from account;
#測試多點回滾
begin;
update account set money= money + 100 where name='A';
SAVEPOINT S1;
update account set money= money + 100 where name='B';
SAVEPOINT S2;
insert into account values(3,'C',1000);
select * from account;
ROLLBACK TO S1;
select * from account;
4.使用 set 設置控制事務
SET AUTOCOMMIT=0; #禁止自動提交
SET AUTOCOMMIT=1; #開啟自動提交,Mysql預設為1
SHOW VARIABLES LIKE 'AUTOCOMMIT'; #查看Mysql中的AUTOCOMMIT值
如果沒有開啟自動提交,當前會話連接的mysql的所有操作都會當成一個事務直到你輸入rollback|commit;當前事務才算結束。當前事務結束前新的mysql連接時無法讀取到任何當前會話的操作結果。 如果開起了自動提交,mysql會把每個sql語句當成一個事務,然後自動的commit。 當然無論開啟與否,begin; commit|rollback; 都是獨立的事務。
use gy;
select * from account;
SET AUTOCOMMIT=0;
update account set money= money + 100 where name='B';
select * from account;
quit
mysql -u root -p
use gy;
select * from account;
----MySQL 存儲引擎---
MyISAM 表支持 3 種不同的存儲格式: (1)靜態(固定長度)表 靜態表是預設的存儲格式。靜態表中的欄位都是非可變欄位,這樣每個記錄都是固定長度的,這種存儲方式的優點是存儲非常迅速,容易緩存,出現故障容易恢復;缺點是占用的空間通常比動態表多。
(2)動態表 動態表包含可變欄位,記錄不是固定長度的,這樣存儲的優點是占用空間較少,但是頻繁的更新、刪除記錄會產生碎片,需要定期執行 OPTIMIZE TABLE 語句或 myisamchk -r 命令來改善性能,並且出現故障的時候恢復相對比較困難。
(3)壓縮表 壓縮表由 myisamchk 工具創建,占據非常小的空間,因為每條記錄都是被單獨壓縮的,所以只有非常小的訪問開支。
常用存儲引擎:InnoDB、MyISAM MyISAM:不支持事務和外鍵約束,占用資源較小,訪問速度快,表級鎖定,支持全文索引,適用於不需要事務處理,單獨寫入或查詢的應用場景。 InnoDB:支持事務處理、外鍵約束,緩存能力較好,支持行級鎖定,讀寫併發能力較好,5.5版本後支持全文索引,適用於一致性要求高、數據更新頻繁的應用場景。
#查看系統支持的存儲引擎
show engines;
#查看表使用的存儲引擎 方法一:
show table status from 庫名 where name='表名'\G
方法二:
use 庫名;
show create table 表名;
#修改存儲引擎 1.通過 alter table 修改
use 庫名;
alter table 表名 engine=MyISAM;
2.通過修改 /etc/my.cnf 配置文件,指定預設存儲引擎並重啟服務
vim /etc/my.cnf
......
[mysqld]
......
default-storage-engine=INNODB
systemctl restart mysql.service
註意:此方法只對修改了配置文件並重啟mysql服務後新創建的表有效,已經存在的表不會有變更。
註意:此方法只對修改了配置文件並重啟mysql服務後新創建的表有效,已經存在的表不會有變更。
3.通過 create table 創建表時指定存儲引擎
use 庫名;
create table 表名(欄位1 數據類型,...) engine=MyISAM;
//InnoDB行鎖與索引的關係
InnoDB行鎖是通過給索引項加鎖來實現的,如果沒有索引,InnoDB將通過隱藏的聚簇索引來對記錄加鎖。
1)
delete from t1 where id=1;
如果id欄位是主鍵,innodb對於主鍵使用了聚簇索引,會直接鎖住整行記錄。
2)
delete from t1 where name='aaa';
如果name欄位是普通索引,會先鎖住索引的兩行,接著會鎖住相應主鍵對應的記錄。
3)
delete from t1 where age=23;
如果age欄位沒有索引,會使用全表掃描過濾,這時表上的各個記錄都將加上鎖。
//死鎖 死鎖一般是事務相互等待對方資源,最後形成環路造成的。
案例:
create table t1(id int primary key, name char(3), age int);
insert into t1 values(1,'aaa',22);
insert into t1 values(2,'bbb',23);
insert into t1 values(3,'aaa',24);
insert into t1 values(4,'bbb',25);
insert into t1 values(5,'ccc',26);
insert into t1 values(6,'zzz',27);
session 1 session 2
begin; begin;
delete from t1 where id=5;
select * from t1 where id=1 for update;
delete from t1 where id=1; #死鎖發生
update t1 set name='abc' where id=5; #死鎖發生
#for update 可以為資料庫中的行上一個排它鎖。當一個事務的操作未完成時候,其他事務可以讀取但是不能寫入或更新。 #共用鎖:又叫做讀鎖,當用戶要進行數據的讀取時,對數據加上共用鎖,共用鎖可以同時加上多個。 #排他鎖:又叫做寫鎖,當用戶要進行數據的寫入時,對數據加上排他鎖,排他鎖只可以加一個,它和其它的排他鎖,共用鎖都相斥。 //如何儘可能避免死鎖? 1)使用更合理的業務邏輯,以固定的順序訪問表和行。 2)大事務拆小。大事務更傾向於死鎖,如果業務允許,將大事務拆小。 3)在同一個事務中,儘可能做到一次鎖定所需要的所有資源,減少死鎖概率。 4)降低隔離級別。如果業務允許,將隔離級別調低也是較好的選擇,比如將隔離級別從RR調整為RC,可以避免掉很多因為gap鎖造成的死鎖。 5)為表添加合理的索引。如果不使用索引將會為表的每一行記錄添加上鎖,死鎖的概率大大增大。