MySQL分享
一、數據庫結構
語句
DDL(Data Definition Languages):數據定義語句,常用的語句關鍵字主要包括 create、drop、alter等操作表結構
DML(Data Manipulation Language):數據操作語句,常用的語句關鍵字主要包括 insert、delete、udpate 和select 等操作數據
DCL(Data Control Language):數據控制語句,用戶的訪問權限和安全級別。主要的語句關鍵字包括 grant、revoke 等
結構
連接者:不同語言的代碼程序和mysql的交互(SQL交互)
連接池:管理、緩沖用戶的連接,線程處理等需要緩存的需求
管理服務和工具組件:系統管理和控制工具,例如備份恢復、Mysql復制、集群等
SQL接口:接受用戶的SQL命令,并且返回用戶需要查詢的結果
解析器:對SQL進行解析,判斷語法是否正確
查詢優化器:SQL語句在查詢之前會使用查詢優化器對查詢進行優化
舉個例子
-- 下面的SQL company 和 name 都有索引,索引類型也相同,字段類型也相同
-- 索引字段能匹配的數據越少,優先使用該索引字段
select * from dept_table where company = '集團' and name = '財務部';
緩存:如果查詢緩存有命中的查詢結果,查詢語句就可以直接去查詢緩存中取數據
存儲引擎:INSERT DELETE UPDATE SELECT 數據的一種方式
存儲引擎名稱 | 特點 | 應用場景 |
---|---|---|
InnoDB | 支持事務、行鎖、支持MVCC多版本并發控制,并發性高 | 應用OLTP業務系統 |
MyISAM | 不支持事務,MySQL8之后被廢棄了,并發很低,資源利用率也很低 | 應用OLAP業務系統,建議生產環境盡量少少使用 MyISAM 存儲引擎 |
MariaDB columnstore | 列式存儲引擎,高壓縮功能 | 數據倉庫,OLAP業務系統 |
數據庫的引擎級別?
- [ ] A. 軟件級別(即安裝MySQL時就已經指定引擎類型)
- [ ] B. 數據庫級別
- [ ] C. 表級別
OLTP(On-Line Transaction Processing):聯機事務處理,比如增刪改查,完成一筆交易處理
OLAP(On-Line Analytical Processing):聯機分析處理,支持復雜的分析操作,側重決策支持,做數據分析,決策,比如 ElasticSearch
SQL執行過程
二、事務
事務特性(ACID)
原子性(A):要么全完成,要么全不完成
一致性(C):在事務開始之前和事務結束以后,數據庫的完整性約束沒有被破壞(比如A給B轉賬100元,A減少100,B增加100)
隔離性(I):事務之間互不干擾
持久性(D):事務提交后保存起來了
隔離級別(對執行事務起作用)
讀未提交(read uncommitted):A事務變更后沒提交,B能看到
讀已提交(read committed):A事務變更后提交,B才能看到
可重復讀(repeatable read):一個事務執行過程中看到的數據,總是跟這個事務在啟動時看到的數據是一致的
串行化(serializable):同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行
并發問題
臟讀:讀未提交的隔離級別下,A事務讀到B更新卻沒commit的數據
不可重復讀:側重修改,A事務兩次讀取同一數據兩次不一致
幻讀:側重數據增減,A事務兩次讀取數據不同
事務隔離級別與并發問題
事務隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|
讀未提交(read uncommitted) | 是 | 是 | 是 |
讀已提交(read committed) | 否 | 是 | 是 |
可重復讀(repeatable read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
驗證事務的隔離級別
事務A | 事務B |
---|---|
begin; | begin; |
select * from dept_table; --V1 | |
select * from dept_table; --V2 | |
update dept_table set name = '研發部' where id = 1; | |
select * from dept_table; --V3 | |
commit; | |
select * from dept_table; --V4 | |
commit; | |
select * from dept_table; --V5 |
事務相關SQL
# 設置事務隔離級別
set session transaction isolation level read uncommitted;set session transaction isolation level read committed;set session transaction isolation level repeatable read;# 查詢當前事務隔離級別
select @@tx_isolation;# 查詢全局事務隔離級別
select @@global.tx_isolation;# 開啟事務
begin;
start transaction;# 提交事務
commit;# 回滾事務
rollback;
隔離級別的實現
多版本并發控制 MVCC(Multi-Version Concurrency Control): MySQL 的 InnoDB 存儲引擎實現隔離級別的一種具體方式,在每行數據都增加兩個隱藏字段,一個記錄創建的版本號,一個記錄刪除的版本號,用于實現提交讀和可重復讀這兩種隔離級別。而未提交讀隔離級別總是讀取最新的數據行,無需使用 MVCC。可串行化隔離級別需要對所有讀取的行都加鎖,單純使用 MVCC 無法實現
版本號
系統版本號:是一個遞增的數字,每開始一個新的事務,系統版本號就會自動遞增
事務版本號:事務開始時的系統版本號
可重復讀的操作
- SELECT:讀取創建版本 <= 當前事務版本號,并且刪除版本為空或大于當前事務版本號的記錄。這樣可以保證在讀取之前記錄是存在的
- INSERT:將當前事務的版本號保存至行的創建版本號
- UPDATE:新插入一行,并以當前事務的版本號作為新行的創建版本號,同時將原記錄行的刪除版本號設置為當前事務版本號
- DELETE:將當前事務的版本號保存至行的刪除版本號
插入一條新數據(事務版本號 = 1)
id | name | create_version | delete_version |
---|---|---|---|
1 | Tom | 1 |
執行更新操作(事務版本號 = 2)
update table set name= 'Tom2' where id = 1;
id | name | create_version | delete_version |
---|---|---|---|
1 | Tom | 1 | 2 |
1 | Tom2 | 2 |
刪除操作(事務版本號 = 3)
delete from table where id = 1;
id | name | create version | delete version |
---|---|---|---|
1 | Tom2 | 2 | 3 |
查詢操作要滿足兩個條件才能查詢出來
- 刪除版本號 大于 當前事務版本號或為空,就是說刪除操作是在當前事務啟動之后做的
- 創建版本號 小于或者等于 當前事務版本號 ,就是說記錄創建是在事務中(等于的情況)或者事務啟動之前
可重復讀情況下的演示
-- 1. T事務(假設:當前事務版本號 = 1)插入一條數據
-- create_version = 1 delete_version = null
insert into `up`.`dept_table`(`id`, `company`, `name`) values (5, '總部', '綜合創新部');-- 1. A事務(假設:當前事務版本號 = 2) 查詢數據 4條結果
select * from dept_table; -- 2. B事務(假設:當前事務版本號 = 3) 更新ID = 1 的數據
update dept_table set name = '金融服務部' where id = 5;-- 3. A事務繼續查詢
select * from dept_table;
快照讀
當執行select操作時 InnoDB默認會執行快照讀,會記錄下這次select后的結果,之后select 的時候就會返回這次快照的數據,即使其他事務提交了不會影響當前select的數據,這就實現了可重復讀了。快照的生成當在第一次執行select的時候,也就是說假設當A開啟了事務,然后沒有執行任何操作,這時候B insert了一條數據然后commit,這時候A執行 select,那么返回的數據中就會有B添加的那條數據。之后無論再有其他事務commit都沒有關系,因為快照已經生成了,后面的select都是根據快照來的
示例1 B事務的查詢結果是否有A新增的數據?
A事務 | B事務 |
---|---|
start transaction; | start transaction; |
insert into up .dept_table (id , company , name ) values (6, '總部', '綜合創新部'); | |
select * from dept_table; | |
commit; | |
select * from dept_table; | |
commit |
示例2 B事務的兩次查詢結果是否相同?第二次查詢是否有A新增的數據?
A事務 | B事務 |
---|---|
start transaction; | start transaction; |
select * from dept_table; | |
insert into up .dept_table (id , company , name ) values (6, '總部', '綜合創新部'); | |
select * from dept_table; | |
commit; | |
select * from dept_table; | |
commit; |
當前讀
對數據修改的操作(update、insert、delete、select …... lock in share mode、select …... for update)都是采用當前讀的模式。在執行這幾個操作時會讀取最新的記錄,即使是別的事務提交的數據也可以查詢到。假設要update一條記錄,但是在另一個事務中已經delete掉這條數據并且commit了,如果update就會產生沖突,所以在update的時候需要知道最新的數據。也正是因為這樣所以才導致上面我們測試的那種情況
示例
A事務 | B事務 |
---|---|
start transaction; | start transaction; |
select * from dept_table; -- V1 | |
insert into up .dept_table (id , company , name ) values (6, '總部', '綜合創新部'); | |
commit; | |
select * from dept_table; -- V2 | |
update dept_table set name = '新部門' where id = 6; | |
select * from dept_table; -- V3 | |
commit; |
快照讀 | 當前讀 |
---|---|
select * from table | select * from table for update |
select * from table lock in share mode | |
insert、delete、insert |
三、InnoDB的鎖
鎖概念
鎖作用
數據庫鎖機制簡單來說,就是數據庫為了保證數據的一致性,使各種共享資源在被并發訪問時變得有序而設計的一種規則
鎖機制
MySQL的鎖機制比較簡單,最顯著的特點時不同的存儲引擎支持不同的鎖機制,我們鎖知道的,InnoDB支持行鎖,有時也會升級為表鎖,MyISAM只支持表鎖
行鎖和表鎖的特點
表鎖 | 行鎖 |
---|---|
開銷小 | 開銷大 |
加鎖快 | 加鎖慢 |
不會出現死鎖 | 會出現死鎖 |
鎖粒度大 | 鎖粒度小 |
發生鎖沖突的概率高 | 發生鎖沖突的概率低 |
并發度相對低 | 并發度相對高 |
InnoDB的鎖類型
讀鎖(共享鎖)
簡稱S鎖,若事務T對數據對象加上讀鎖,則事務T可以讀但不能修改,其他事務只能再對該數據加讀鎖不能加寫鎖,直到T釋放該數據的讀鎖。即一個事務在讀取一個數據行時,其他事務也可以讀,但不能對該數據進行增刪改的操作
讀鎖的實現方式
- 自動提交模式下的select查詢語句,不需要加任何鎖,直接返回查詢結果,這是一致性非鎖定讀
- 通過 select ...... lock in share mode 在被讀取的行記錄或行記錄的范圍上加一個鎖,讓其他事務可以讀,但是想要申請寫鎖,就要被阻塞
-- 查詢是否自動提交模式
show variables like '%auto%';
寫鎖(排它鎖)
簡稱X鎖,若事務T對數據對象加上寫鎖,事務T可以讀也可以修改,其他事務不能再對該數據加任何鎖,不能讀也不能寫,直到T釋放該數據上的鎖。即一個事務獲取了一個數據行的寫鎖,其他事務就不能再獲取該行的其他鎖,寫鎖優先級最高
寫鎖的實現方式
- INSERT、DELETE、UPDATE語句的操作都會對行記錄加寫鎖
- 通過 select ...... for update 對讀取的記錄行上加寫鎖,其他任何事務就不能對鎖定的行上加任何鎖了,否則會被阻塞
MDL鎖(meta data lock)
在事務A中開啟查詢,會自動獲得一個MDL鎖,事務B就不可以執行任何DDL語句的操作
事務A | 事務B |
---|---|
begin; | begin; |
select * from score_table; | |
alter table score_table add num tinyint(1) not null default 0; -- 阻塞 | |
commit; | |
commit; |
-- 查詢進程列表
show full processlist;
意向鎖
在MySQL存儲引擎InnoDB中,意向鎖是表級鎖。而且有兩種意向鎖類型,分別為意向共享鎖和意向排他鎖
- 意向共享鎖(IS):是指在給一個數據行加共享鎖前必須先取得該表的IS鎖
- 意向排他鎖(IX):是指在給一個數據行加排他鎖前必須先取得該表的IX鎖
其實意向鎖的作用跟MDL鎖類似,都是防止在事務進行過程中,執行DDL語句的操作而導致數據不一致
InnoDB行鎖種類
在默認的事務隔離級別為RR,并且參數 innodb_locks_unsafe_for_binlog = 0 的模式下,行鎖的種類有三種
- 單個行記錄鎖(record lock):鎖定一個行記錄
注:主鍵和唯一索引都是行記錄的鎖模式。在RC隔離級別下,只有 record lock 記錄鎖模式
- 間隙鎖(gap lock):鎖定一個區間
- 記錄鎖和間隙鎖的組合叫做 next-key lock:鎖定行記錄 + 區間
注:普通索引默認的就是 next-key lock模式
單個行記錄的鎖
-- 查看索引
show index from dept_table;
company 字段有索引
事務A | 事務B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'aaa' where score= 60; -- 出現鎖等待 | |
update score_table set name = 'bbb' where score= 70; -- 正常更新 | |
commit; | |
commit; |
company 字段無索引
事務A | 事務B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'bbb' where score= 70; -- 出現鎖等待 | |
commit; | |
commit; |
InnoDB的鎖是加在索引上的
間隙鎖
在RR事務隔離級別,為了避免幻讀現象,引入了 gap lock,但它只鎖定記錄的范圍,不包含記錄本身,即不允許在次范圍內插入任何數據
事務隔離級別RR
事務A | 事務B |
---|---|
begin; | begin; |
select * from score_table where score < 80 lock in share mode; | |
insert into score_table(name, score) values ('dd', 75); -- 等待 | |
commit; | |
commit; |
事務隔離級別RC
-- 查看事務隔離級別
show variables like '%tx_isolation%';
事務A | 事務B |
---|---|
begin; | begin; |
set session transaction isolation level read committed; | set session transaction isolation level read committed; |
select * from score_table where score < 80 lock in share mode; | |
insert into score_table(name, score) values ('dd', 75); -- 可提交 | |
commit; | |
commit; |
證明間隙鎖只是針對RR隔離級別才管用,從鎖的角度避免了幻讀發生
next-key locks
記錄鎖與間隙鎖的組合,當InnoDB掃描表記錄時,會先對選中的索引加上記錄鎖,再對索引記錄兩邊的間隙加上間隙鎖
事務A | 事務B |
---|---|
begin; | begin; |
select * from score_table where score < 85 for update; | |
insert into score_table(name, score) values ('dd', 85); -- 等待 | |
commit; | |
commit; |
證明不光鎖定 score < 85 這個區間的數據,還包含 85 這個值的本身
鎖等待和死鎖
鎖等待是指一個事務過程中產生的鎖,其他事務需要等待上一個事務釋放它的鎖,才能占用該資源。如果該事務一直不釋放,就需要持續等待下去,直到超過鎖等待時間,會報一個等待超時的錯誤
-- 查詢鎖等待時間
show variables like '%innodb_lock_wait%';-- 修改全局變量方式
set global innodb_lock_wait_timeout = 2;set @@global.innodb_lock_wait_timeout = 5;-- 修改當前事務變量方式
set innodb_lock_wait_timeout = 2;
死鎖是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,即事務1鎖住數據D1,正請求對D2加鎖,而事務2鎖住了D2,正請求加鎖D1,這樣就會導致死鎖
事務A | 事務B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'bbb' where score= 70; | |
update score_table set name = 'b' where score= 70; | |
update score_table set name = 'aaa' where score= 60; | |
commit; | |
commit; |
-- 查看死鎖展示信息
show engine innodb status;
解決死鎖
- 如果不同程序并發存取多個表,或者涉及多行記錄時,盡量約定以相同的順序訪問表,可以大大降低死鎖的機會
- 業務中盡量采用小事務,避免使用大事務,要及時提交或者回滾事務,可減少死鎖產生概率
- 在同一個事務中,盡可能做到一次鎖定所需要的所有資源,減少死鎖產生概率
- 對于非常容易產生死鎖的業務部分,可以嘗試使用升級鎖粒度,通過表鎖定來減少死鎖產生概率
四、索引
理解索引
在工作中,開發人員在開發初期由于多種原因設計表的過程中,沒有給后面可能會經常訪問的字段增加索引這個概念,由于后期的業務調整需求變更且數據也在不斷增長,會根據某些已存在的字段作為條件查詢。后面再增加索引會很麻煩,需要考慮現有系統的可用性,還需要考慮到當前增加索引列的內容。所以創建索引需要結合現有業務還要預知后期可能存在的業務變更,使設計的數據表伸縮性更強,在設計初期把數據表設計完善也能夠讓后面的開發事半功倍。
索引利用好了就是一輛”法拉利“,利用不好就成了”三輪車“了。
索引是什么?為什么要使用索引?
幫助MySQL做高效查詢的一種數據結構,好比是一本書的目錄,通過目錄能快速找到要查看的內容
提高查詢效率
索引分類
從存儲結構劃分:BTree索引(B-Tree或B+Tree索引),Hash索引
從應用層劃分:主鍵索引,唯一索引,普通索引,復合索引,全文索引,SPATIAL
根據鍵值的邏輯順序和表數據行的物理存儲順序:聚集索引,非聚集索引
聚集索引:葉子節點存放表中所有行數據記錄的信息
非聚集索引:不是聚集索引就是非聚集索引
索引何時無效?
索引一般在 where,order by,group by,表連接的條件列起作用
索引無效:
-- 1. 查詢條件的值是null
explain select * from dept_table where name = null;-- 2. NOT條件 比如:<>、in、not in、exists、not exists
explain select * from dept_table where name <> '新部門';explain select * from dept_table where name in ('財務部2'); -- 可以使用索引explain select * from dept_table where name in ('財務部2', '新部門'); -- 不使用索引explain select * from dept_table where name not in ('新部門'); -- 一個和多個都不使用索引explain select * from dept_table where exists (select * from dept_table where name = '新部門');explain select * from dept_table where not exists (select * from dept_table where name = '新部門');-- 3. LIKE通配符在前面
explain select * from dept_table where name like '%部門';explain select * from dept_table where name like '部門%'; -- 可以使用索引explain select * from dept_table where name like '新%門%'; -- 可以使用索引-- 4. 條件列上包括函數
explain select * from dept_table where upper(name) = 'NEW';explain select * from dept_table where name = upper('new'); -- 可以使用索引
索引存到哪里了?用什么方式存儲?
索引是存儲到磁盤上的文件
MyISAM引擎和InnoDB引擎共有文件
.frm文件:在MYSQL中建立任何一張數據表,在其數據目錄對應的數據庫目錄下都有對應表的.frm文件,.frm文件是用來保存每個數據表的元數據(meta)信息,包括表結構的定義等,.frm文件跟數據庫存儲引擎無關,也就是任何存儲引擎的數據表都必須有.frm文件,命名方式為數據表名.frm,如user.frm. .frm文件可以用來在數據庫崩潰時恢復表結構
InnoDB引擎文件
.ibd文件:單表表空間文件,每個表使用一個表空間文件(file per table),存放用戶數據庫表數據和索引
MyISAM引擎
.MYD:即 my data,表數據文件
.MYI:即 my index,索引文件
索引原理
MySQL為什么沒有使用二叉樹或紅黑樹?
推薦一個數據結構網站:www.cs.usfca.edu/~galles/visualization/Algorithms.html
如有 3、1、2、10、9、0、4、6這8個數據
哈希結構
- 直接查詢:現在要從8個數中查找6這條記錄,只需要計算6的哈希值,便可快速定位記錄,時間復雜度為O(1)
- 范圍查詢:如果要進行范圍查詢(大于4的數據),那這個索引就完全沒用了
二叉樹結構(右子樹大于左子樹)
- 直接查詢:現在要從8個數中查找10這條記錄,先查找3,6>3,查找10,查找兩次就可以了
- 范圍查詢:如果要進行范圍查詢(大于3的數據),直接查詢大于3的右子樹就行了
不使用二叉樹原因:如果索引列查入的數據是單邊增長的,會導致查詢時依舊變慢,最差情況時間復雜度會變成O(N)
紅黑樹結構(是一種平衡的二叉查找樹)
特點:
- 根節點是黑色
- 節點是紅色或者黑色
- 每個葉子節點都是黑色的空節點(null)
- 每個紅色節點的兩個子節點都是黑色的
- 從任意節點到其每個葉子的所有路徑都包含相同數量的黑色節點
- 直接查詢:現在要從10個數中查找10這條記錄,先查找4,10>4,查找6,10>6,查找到8,10>8,查找到9,10>9,查找到10
- 范圍查詢:如果要進行范圍查詢(大于6的數據),直接查詢大于6的右子樹就行了
- 問題:當數據量變大后,樹的高度依然是很大,讀取I/O磁盤次數還是很多
為什么想要減少磁盤I/O次數?
MySQL的數據實際是存儲在文件中,而磁盤IO的查找速度是要遠小于內存速度的,所以減少磁盤IO的次數能很大程度的提高MySQL性能
磁盤I/O為什么慢?
磁盤IO時間 = 尋道 + 磁盤旋轉 + 數據傳輸時間
從磁盤讀取數據時,系統會將邏輯地址發給磁盤,磁盤將邏輯地址轉換為物理地址(哪個磁道,哪個扇區)。 磁頭進行機械運動,先找到相應磁道,再找該磁道的對應扇區,扇區是磁盤的最小存儲單元
性能對比
機械硬盤的連續讀寫性能很好,但隨機讀寫性能很差。
- 順序訪問:內存訪問速度是硬盤訪問速度的6~7倍
- 隨機訪問:內存訪問速度就要比硬盤訪問速度快上10萬倍以上
隨機讀寫時,磁頭需要不停的移動,時間都浪費在了磁頭尋址上。 而在實際的磁盤存儲里,是很少順序存儲的,因為這樣的維護成本會很高
局部性原理與磁盤預讀
由于存儲介質的特性,磁盤本身存取就比主存慢很多,再加上機械運動耗費,磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁盤I/O。為了達到這個目的,磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節,磁盤也會從這個位置開始,順序向后讀取一定長度的數據放入內存。這樣做的理論依據是計算機科學中著名的局部性原理:
當一個數據被用到時,其附近的數據也通常會馬上被使用。
程序運行期間所需要的數據通常比較集中。
由于磁盤順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對于具有局部性的程序來說,預讀可以提高I/O效率。
預讀的長度一般為頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操作系統往往將主存和磁盤存儲區分割為連續的大小相等的塊,每個存儲塊稱為一頁(在許多操作系統中,頁得大小通常為4k),主存和磁盤以頁為單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置并向后連續讀取一頁或幾頁載入內存中,然后異常返回,程序繼續運行。
BTree(B-Tree)和B+Tree閃亮登場
度(m):節點數據存儲個數
深度關系(h):h=log(m+1)N
B+Tree相對于BTree區別:
- 非葉子節點只存儲索引鍵,從而降低B+樹的高度,進而減少IO次數
- 葉子節點存儲整張表的所有行數
MyISAM索引實現
MyISAM引擎使用B+Tree作為索引結構,葉節點的data域存放的是數據記錄的地址
這里設表一共有三列,假設我們以Col1為主鍵,上圖是一個MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件僅僅保存數據記錄的地址。在MyISAM中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求key是唯一的,而輔助索引的key可以重復。如果我們在Col2上建立一個輔助索引,則此索引的結構如下圖所示
同樣也是一顆B+Tree,data域保存數據記錄的地址。因此,MyISAM中索引檢索的算法為首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,則取出其data域的值,然后以data域的值為地址,讀取相應數據記錄
所以MyISAM是非“聚集索引”,從索引文件和數據文件分別存儲就可以看出。也與下面介紹的InnoDB的“聚集索引”做區分
InnoDB索引實現
雖然InnoDB也使用B+Tree作為索引結構,但具體實現方式卻與MyISAM截然不同
第一個不同:MyISAM索引文件和數據文件是分離的,索引文件僅保存數據記錄的地址。InnoDB的數據文件本身就是索引文件,從文件存儲上就可以知道,表數據文件本身就是按B+Tree組織的一個索引結構,這棵樹的葉節點data域保存了完整的數據記錄。這個索引的key是數據表的主鍵,因此InnoDB表數據文件本身就是主索引
? InnoDB主索引(同時也是數據文件)的示意圖
可以看到葉節點包含了完整的數據記錄。這種索引叫做聚集索引。因為InnoDB的數據文件本身要按主鍵聚集,所以InnoDB要求表必須有主鍵(MyISAM可以沒有),如果沒有顯式指定,則MySQL系統會自動選擇一個可以唯一標識數據記錄的列作為主鍵,如果不存在這種列,則MySQL自動為InnoDB表生成一個隱含字段作為主鍵,這個字段長度為6個字節,類型為長整形。
第二個不同:與MyISAM索引的不同是InnoDB的輔助索引data域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB的所有輔助索引都引用主鍵作為data域
? InnoDB輔助索引(輔助索引獲得主鍵索引)的示意圖
這里以英文字符的ASCII碼作為比較準則。聚集索引這種實現方式使得按主鍵的搜索十分高效,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然后用主鍵到主索引中檢索獲得記錄。
問題
- InnoDB引擎為什么必須有主鍵且建議使用整型自增主鍵?
空間方面:uuid存儲空間更大
比較關系:索引之間有比較關系,整型的比較會更快
結構方面(最重要一點):使用自增主鍵保證插入數據都是向葉子節點后面順序插入,如果使用uuid比較,生成的uuid不一定順序插入,導致插入到節點的中間位置,如果該節點已經滿了,會導致節點進行分裂變成新的結構
- 為什么InnoDB引擎的非主鍵索引數據存儲的是主鍵值?
一致性和節省存儲空間
- 節點的度設置為多少合適?
B-Tree節點的度設置等于一個頁,每次新建節點直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁里,就實現了一個節點的載入只需要載入一次I/O
- 聯合索引的底層存儲結構是長什么樣?
四、總結
- 數據庫結構、一條SQL執行過程
- 事務隔離級別、并發問題、可重復讀的實現過程
- 鎖的使用、鎖等待、死鎖
- 索引分類、索引如何存儲、索引何時無效、MySQL為什么使用B+Tree作為索引結構、MyISAM索引實現、InnoDB索引實現
- 磁盤讀寫過程
- MySQL主從復制過程
五、主從復制
事務相關的重要日志
redolog:數據日志
undolog:ibdata1
binlog:邏輯日志