學(xué)習(xí)筆記:MySQL技術(shù)內(nèi)幕:InnoDB存儲引擎(姜承堯)

觀其大綱

第1章 MySQL體系結(jié)構(gòu)和存儲引擎
第2章 InnoDB存儲引擎
第3章 文件
第4章 表
第5章 索引與算法
第6章 鎖
第7章 事務(wù)
第8章 備份與恢復(fù)
第9章 性能調(diào)優(yōu)
第10章 InnoDB存儲引擎源代碼的編譯和調(diào)試

大綱細(xì)節(jié)

第1章 MySQL體系結(jié)構(gòu)和存儲引擎 1
1.1 定義數(shù)據(jù)庫和實例 1
1.2 MySQL體系結(jié)構(gòu) 3
1.3 MySQL存儲引擎 5
1.3.1 InnoDB存儲引擎 6
1.3.2 MyISAM存儲引擎 7
1.3.3 NDB存儲引擎 7
1.3.4 Memory存儲引擎 8
1.3.5 Archive存儲引擎 9
1.3.6 Federated存儲引擎 9
1.3.7 Maria存儲引擎 9
1.3.8 其他存儲引擎 9
1.4 各存儲引擎之間的比較 10
1.5 連接MySQL 13
1.5.1 TCP/IP 13
1.5.2 命名管道和共享內(nèi)存 15
1.5.3 UNIX域套接字 15
1.6 小結(jié) 15
第2章 InnoDB存儲引擎 17
2.1 InnoDB存儲引擎概述 17
2.2 InnoDB存儲引擎的版本 18
2.3 InnoDB體系架構(gòu) 19
2.3.1 后臺線程 19
2.3.2 內(nèi)存 22
2.4 Checkpoint技術(shù) 32
2.5 Master Thread工作方式 36
2.5.1 InnoDB 1.0.x版本之前的Master Thread 36
2.5.2 InnoDB1.2.x版本之前的Master Thread 41
2.5.3 InnoDB1.2.x版本的Master Thread 45
2.6 InnoDB關(guān)鍵特性 45
2.6.1 插入緩沖 46
2.6.2 兩次寫 53
2.6.3 自適應(yīng)哈希索引 55
2.6.4 異步IO 57
2.6.5 刷新鄰接頁 58
2.7 啟動、關(guān)閉與恢復(fù) 58
2.8 小結(jié) 61
第3章 文件 62
3.1 參數(shù)文件 62
3.1.1 什么是參數(shù) 63
3.1.2 參數(shù)類型 64
3.2 日志文件 65
3.2.1 錯誤日志 66
3.2.2 慢查詢?nèi)罩?67
3.2.3 查詢?nèi)罩?72
3.2.4 二進(jìn)制日志 73
3.3 套接字文件 83
3.4 pid文件 83
3.5 表結(jié)構(gòu)定義文件 84
3.6 InnoDB存儲引擎文件 84
3.6.1 表空間文件 85
3.6.2 重做日志文件 86
3.7 小結(jié) 90
第4章 表 91
4.1 索引組織表 91
4.2 InnoDB邏輯存儲結(jié)構(gòu) 93
4.2.1 表空間 93
4.2.2 段 95
4.2.3 區(qū) 95
4.2.4 頁 101
4.2.5 行 101
4.3 InnoDB行記錄格式 102
4.3.1 Compact行記錄格式 103
4.3.2 Redundant行記錄格式 106
4.3.3 行溢出數(shù)據(jù) 110
4.3.4 Compressed和Dynamic行記錄格式 117
4.3.5 CHAR的行結(jié)構(gòu)存儲 117
4.4 InnoDB數(shù)據(jù)頁結(jié)構(gòu) 120
4.4.1 File Header 121
4.4.2 Page Header 122
4.4.3 Infimum和Supremum Records 123
4.4.4 User Records和Free Space 123
4.4.5 Page Directory 124
4.4.6 File Trailer 124
4.4.7 InnoDB數(shù)據(jù)頁結(jié)構(gòu)示例分析 125
4.5 Named File Formats機(jī)制 132
4.6 約束 134
4.6.1 數(shù)據(jù)完整性 134
4.6.2 約束的創(chuàng)建和查找 135
4.6.3 約束和索引的區(qū)別 137
4.6.4 對錯誤數(shù)據(jù)的約束 137
4.6.5 ENUM和SET約束 139
4.6.6 觸發(fā)器與約束 139
4.6.7 外鍵約束 142
4.7 視圖 144
4.7.1 視圖的作用 144
4.7.2 物化視圖 147
4.8 分區(qū)表 152
4.8.1 分區(qū)概述 152
4.8.2 分區(qū)類型 155
4.8.3 子分區(qū) 168
4.8.4 分區(qū)中的NULL值 172
4.8.5 分區(qū)和性能 176
4.8.6 在表和分區(qū)間交換數(shù)據(jù) 180
4.9 小結(jié) 182
第5章 索引與算法 183
5.1 InnoDB存儲引擎索引概述 183
5.2 數(shù)據(jù)結(jié)構(gòu)與算法 184
5.2.1 二分查找法 184
5.2.2 二叉查找樹和平衡二叉樹 185
5.3 B+樹 187
5.3.1 B+樹的插入操作 187
5.3.2 B+樹的刪除操作 190
5.4 B+樹索引 191
5.4.1 聚集索引 192
5.4.2 輔助索引 196
5.4.3 B+樹索引的分裂 200
5.4.4 B+樹索引的管理 202
5.5 Cardinality值 210
5.5.1 什么是Cardinality 210
5.5.2 InnoDB存儲引擎的Cardinality統(tǒng)計 212
5.6 B+樹索引的使用 215
5.6.1 不同應(yīng)用中B+樹索引的使用 215
5.6.2 聯(lián)合索引 215
5.6.3 覆蓋索引 218
5.6.4 優(yōu)化器選擇不使用索引的情況 219
5.6.5 索引提示 221
5.6.6 Multi-Range Read優(yōu)化 223
5.6.7 Index Condition Pushdown(ICP)優(yōu)化 226
5.7 哈希算法 227
5.7.1 哈希表 228
5.7.2 InnoDB存儲引擎中的哈希算法 229
5.7.3 自適應(yīng)哈希索引 230
5.8 全文檢索 231
5.8.1 概述 231
5.8.2 倒排索引 232
5.8.3 InnoDB全文檢索 233
5.8.4 全文檢索 240
5.9 小結(jié) 248
第6章 鎖 249
6.1 什么是鎖 249
6.2 lock與latch 250
6.3 InnoDB存儲引擎中的鎖 252
6.3.1 鎖的類型 252
6.3.2 一致性非鎖定讀 258
6.3.3 一致性鎖定讀 261
6.3.4 自增長與鎖 262
6.3.5 外鍵和鎖 264
6.4 鎖的算法 265
6.4.1 行鎖的3種算法 265
6.4.2 解決Phantom Problem 269
6.5 鎖問題 271
6.5.1 臟讀 271
6.5.2 不可重復(fù)讀 273
6.5.3 丟失更新 274
6.6 阻塞 276
6.7 死鎖 278
6.7.1 死鎖的概念 278
6.7.2 死鎖概率 280
6.7.3 死鎖的示例 281
6.8 鎖升級 283
6.9 小結(jié) 284
第7章 事務(wù) 285
7.1 認(rèn)識事務(wù) 285
7.1.1 概述 285
7.1.2 分類 287
7.2 事務(wù)的實現(xiàn) 294
7.2.1 redo 294
7.2.2 undo 305
7.2.3 purge 317
7.2.4 group commit 319
7.3 事務(wù)控制語句 323
7.4 隱式提交的SQL語句 328
7.5 對于事務(wù)操作的統(tǒng)計 329
7.6 事務(wù)的隔離級別 330
7.7 分布式事務(wù) 335
7.7.1 MySQL數(shù)據(jù)庫分布式事務(wù) 335
7.7.2 內(nèi)部XA事務(wù) 340
7.8 不好的事務(wù)習(xí)慣 341
7.8.1 在循環(huán)中提交 341
7.8.2 使用自動提交 343
7.8.3 使用自動回滾 344
7.9 長事務(wù) 347
7.10 小結(jié) 349
第8章 備份與恢復(fù) 350
8.1 備份與恢復(fù)概述 350
8.2 冷備 352
8.3 邏輯備份 353
8.3.1 mysqldump 353
8.3.2 SELECT...INTO OUTFILE 360
8.3.3 邏輯備份的恢復(fù) 362
8.3.4 LOAD DATA INFILE 362
8.3.5 mysqlimport 364
8.4 二進(jìn)制日志備份與恢復(fù) 366
8.5 熱備 367
8.5.1 ibbackup 367
8.5.2 XtraBackup 368
8.5.3 XtraBackup實現(xiàn)增量備份 370
8.6 快照備份 372
8.7 復(fù)制 376
8.7.1 復(fù)制的工作原理 376
8.7.2 快照+復(fù)制的備份架構(gòu) 380
8.8 小結(jié) 382
第9章 性能調(diào)優(yōu) 383
9.1 選擇合適的CPU 383
9.2 內(nèi)存的重要性 384
9.3 硬盤對數(shù)據(jù)庫性能的影響 387
9.3.1 傳統(tǒng)機(jī)械硬盤 387
9.3.2 固態(tài)硬盤 387
9.4 合理地設(shè)置RAID 389
9.4.1 RAID類型 389
9.4.2 RAID Write Back功能 392
9.4.3 RAID配置工具 394
9.5 操作系統(tǒng)的選擇 397
9.6 不同的文件系統(tǒng)對數(shù)據(jù)庫性能的影響 398
9.7 選擇合適的基準(zhǔn)測試工具 399
9.7.1 sysbench 399
9.7.2 mysql-tpcc 405
9.8 小結(jié) 410
第10章 InnoDB存儲引擎源代碼的編譯和調(diào)試 411
10.1 獲取InnoDB存儲引擎源代碼 411
10.2 InnoDB源代碼結(jié)構(gòu) 413
10.3 MySQL 5.1版本編譯和調(diào)試InnoDB源代碼 415
10.3.1 Windows下的調(diào)試 415
10.3.2 Linux下的調(diào)試 418
10.4 cmake方式編譯和調(diào)試InnoDB存儲引擎 423
10.5 小結(jié) 424

熟知概念

第1章 MySQL體系結(jié)構(gòu)和存儲引擎

定義數(shù)據(jù)庫和實例

數(shù)據(jù)庫(database):物理操作系統(tǒng)文件或其他形式文件的集合;是依照某種數(shù)據(jù)模型組織起來并存放于二級存儲器中的數(shù)據(jù)集合。通常表現(xiàn)為:frm、MYD、MYI、ibd結(jié)尾的文件。
實例(instance):MySQL數(shù)據(jù)庫由后臺線程及一個共享內(nèi)存區(qū)組成。共享內(nèi)存可以被運(yùn)行的后臺線程所共享。數(shù)據(jù)庫實例是真正用于操作數(shù)據(jù)庫文件的。
通俗理解:
實例:程序,是位于用戶與操作系統(tǒng)之間的一層數(shù)據(jù)管理軟件,用戶對與數(shù)據(jù)庫數(shù)據(jù)的任何操作(增刪改查),都是在數(shù)據(jù)庫實例下進(jìn)行的,應(yīng)用程序只有通過數(shù)據(jù)庫實例才能和數(shù)據(jù)庫打交道。
數(shù)據(jù)庫:由一個個文件組成(一般來說都是二進(jìn)制文件),要對這些文件進(jìn)行增刪改查,則需要通過數(shù)據(jù)庫實例來完成。
關(guān)系:MySQL數(shù)據(jù)庫中,實例和數(shù)據(jù)庫的關(guān)系通常是一一對應(yīng)的,即一個實例對應(yīng)一個數(shù)據(jù)庫。集群情況下,可能存在一個數(shù)據(jù)庫被多個實例使用的情況。
MySQL數(shù)據(jù)庫實例在系統(tǒng)上的表現(xiàn)為:一個進(jìn)程。

MySQL體系結(jié)構(gòu)

image

由圖可見:mysql由以下組成
Connection Pool:連接池組件,管理緩沖用戶連接,線程處理等需要緩存的需求
Management Serveices &Utilities:管理服務(wù)和工具組件
SQL Interface:SQL接口組件,接受用戶的SQL命令,并且返回用戶需要查詢的結(jié)果。比如select from就是調(diào)用SQL Interface。

Parser:查詢分析器組件SQL命令傳遞到解析器的時候會被解析器驗證和解析。解析器是由Lex和YACC實現(xiàn)的,是一個很長的腳本。

  • 主要功能:
    a . 將SQL語句分解成數(shù)據(jù)結(jié)構(gòu),并將這個結(jié)構(gòu)傳遞到后續(xù)步驟,以后SQL語句的傳遞和處理就是基于這個結(jié)構(gòu)的 。
    b. 如果在分解構(gòu)成中遇到錯誤,那么就說明這個sql語句是不合理的

Optimizer:優(yōu)化器組件,QL語句在查詢之前會使用查詢優(yōu)化器對查詢進(jìn)行優(yōu)化。他使用的是“選取-投影-聯(lián)接”策略進(jìn)行查詢。用一個例子就可以理解: select uid,name fromuser where gender = 1;這個select 查詢先根據(jù)where 語句進(jìn)行選取,而不是先將表全部查詢出來以后再進(jìn)行g(shù)ender過濾。這個select查詢先根據(jù)uid和name進(jìn)行屬性投影,而不是將屬性全部取出以后再進(jìn)行過濾,將這兩個查詢條件聯(lián)接起來生成最終查詢結(jié)果
Cache和Buffer:緩沖組件,如果查詢緩存有命中的查詢結(jié)果,查詢語句就可以直接去查詢緩存中取數(shù)據(jù)。這個緩存機(jī)制是由一系列小緩存組成的。比如表緩存,記錄緩存,key緩存,權(quán)限緩存等
Engine:插件式存儲引擎,存儲引擎是MySql中具體的與文件打交道的子系統(tǒng)。也是Mysql最具有特色的一個地方。Mysql的存儲引擎是插件式的。它根據(jù)MySql AB公司提供的文件訪問層的一個抽象接口來定制一種文件訪問機(jī)制(這種訪問機(jī)制就叫存儲引擎)。MySQL插件式的存儲引擎架構(gòu)提供了一系列標(biāo)準(zhǔn)的管理和服務(wù)支持,這些標(biāo)準(zhǔn)和存儲引擎本書無關(guān)。注意:存儲引擎是基于表的,而不是基于數(shù)據(jù)庫
物理文件:不展開。

MYSQL存儲引擎

好處:每個存儲引擎都有各自的特點(diǎn),能夠根據(jù)具體的應(yīng)用建立不同存儲引擎表。

InnoDB

1.從mysql5.5.8開始,InnoDB是默認(rèn)的存儲引擎。
2.設(shè)計目標(biāo):面向在線事物處理(OLTP)
3.特點(diǎn):行鎖設(shè)計,支持外鍵,支持非鎖定讀。即默認(rèn)讀取操作不會產(chǎn)生鎖。
InnoDB把數(shù)據(jù)放在一個邏輯的表空間中,這個表空間像黑盒一樣由InnoDB進(jìn)行管理??梢詫⒚總€InnoDB存儲引擎的表單獨(dú)存放到一個獨(dú)立的ibd文件中。支持用裸設(shè)備(row disk)來建立其表空間。通過多版本并發(fā)控制來獲得高并發(fā)性。實現(xiàn)了SQL標(biāo)準(zhǔn)的4中隔離級別,默認(rèn)為REPEATABLE級別。對表中數(shù)據(jù)的存儲,采用了聚集的方式。因此,每張表的存儲都是按照主鍵的順序進(jìn)行存放。如果沒有顯式的在表定義中指定主鍵,InnoDB存儲引擎會為每一行生成一個6字節(jié)的ROWID,并以此作為主鍵。

MyISAM存儲引擎

不支持事務(wù)、表鎖設(shè)計,支持全文索引,主要面向一些OLAP數(shù)據(jù)庫應(yīng)用。5.5.8之前,mysql的默認(rèn)存儲引擎。另外,它的緩沖池只緩沖索引文件,而不緩沖數(shù)據(jù)文件。MyISAM存儲引擎表由MYD和MYI組成,MYD用來存放數(shù)據(jù)文件,MYI用來存放索引文件。

NDB存儲引擎

集群存儲引擎,其結(jié)構(gòu)是share nothing的集群機(jī)構(gòu),因此能提供更高的可用性。特點(diǎn)是數(shù)據(jù)全部放在內(nèi)存中,5.1開始,可以將非索引數(shù)據(jù)放在磁盤上,因此主鍵查找的速度極快,并通過添加NDB數(shù)據(jù)存儲節(jié)點(diǎn)可以線性的提高數(shù)據(jù)庫性能,是高可用,高性能的集群系統(tǒng)。
NDB存儲引擎的鏈接操作(JOIN)是在mysql數(shù)據(jù)庫層完成的,而不是在存儲引擎層完成的。這意味著復(fù)雜的鏈接操作需要巨大的網(wǎng)絡(luò)開銷,因此查詢速度很慢。

Memory存儲引擎

將表中數(shù)據(jù)存放在內(nèi)存中,如果數(shù)據(jù)庫重啟或發(fā)生奔潰,表中數(shù)據(jù)都將消失。適合存數(shù)據(jù)臨時表及數(shù)據(jù)倉庫中的緯度表。默認(rèn)使用哈希索引,而不是B+樹索引。只支持表鎖,并發(fā)性能較差,不支持TEXT和BLOB列類型。存儲變長(varchar)字段時是按照定長(char)字段的方式進(jìn)行的,浪費(fèi)內(nèi)存。

Archive存儲引擎

只支持insert和select操作,5.1以后支持索引。使用zlib算法將數(shù)據(jù)行進(jìn)行壓縮后存儲,壓縮比可達(dá)1:10。適合存儲歸檔數(shù)據(jù),如日志信息。使用行鎖來實現(xiàn)高并發(fā)的插入操作,但是其本身不是事務(wù)安全的存儲引擎,設(shè)計目標(biāo)主要是提供高速的插入和壓縮功能。

Federated存儲引擎

不存放數(shù)據(jù),只是指向一臺遠(yuǎn)程mysql數(shù)據(jù)庫服務(wù)器上的表。只支持mysql數(shù)據(jù)庫表,不支持異構(gòu)數(shù)據(jù)庫表。

Maria存儲引擎

新開發(fā)的引擎,主要是用來取代原有的MyISAM存儲引擎,從而成為MySQL的默認(rèn)存儲引擎。支持緩存數(shù)據(jù)和索引文件,應(yīng)用了行鎖設(shè)計,提供了MVCC功能,支持事務(wù)和非事務(wù)安全的選項,以及更好的BLOB字符類型的處理性能。
其他存儲引擎:不展開
show engines:查看當(dāng)前mysql所支持的存儲引擎。
通過查找information_schema架構(gòu)下的engines表查看當(dāng)前mysql所支持的存儲引擎。
create table mytest Engine=MyISAM;
create table mytest Engine=InnoDB;
create table mytest Engine=ARCHIVE;

連接MySQL

一個連接進(jìn)程和MySQL數(shù)據(jù)庫實例進(jìn)行通信。本質(zhì)上是進(jìn)程通信。
TCP/IP:我們平時所用的連接方式。通過TCP/IP連接到MySQL實例時,MySQL數(shù)據(jù)庫會先檢查一張權(quán)限視圖,用來判斷發(fā)起請求的客戶端IP是否允許連接到MySQL實例。該視圖在mysql架構(gòu)下,表名為user.

命名管道和共享內(nèi)存:

如果兩個需要進(jìn)程通信的進(jìn)程在同一臺服務(wù)器上,那么可以適用命名管道,在配置文件中啟用--enable-named-pipe選項。
共享內(nèi)存的連接方式:通過在配置文件中添加--shared-memory實現(xiàn)。如果想使用共享內(nèi)存的方式,在連接時,MySQL客戶端還必須使用--protocol=memory選項。

UNIX域套接字:

Linux或UNIX環(huán)境下,mysql客戶端和數(shù)據(jù)庫實例在一臺服務(wù)器上的情況下可以使用。用戶可以在配置文件中指定套接字文件的路徑,如--socket=/tmp/mysql.sock.當(dāng)數(shù)據(jù)庫實例啟動后,用戶可以通過下列命令來進(jìn)行UNIX域套接字文件的查找:
mysql > SHOW VARTABLES LIKE 'socket';
感覺這種連接方式不會常用的。

第2章 InnoDB存儲引擎

Innodb體系結(jié)構(gòu)

首先以一張圖簡單展示 InnoDB 的存儲引擎的體系架構(gòu).從圖中可見, InnoDB 存儲引擎有多個內(nèi)存塊,這些內(nèi)存塊組成了一個大的內(nèi)存池,主要負(fù)責(zé)如下工作:

  • 維護(hù)所有進(jìn)程/線程需要訪問的多個內(nèi)部數(shù)據(jù)結(jié)構(gòu)
  • 緩存磁盤上的數(shù)據(jù), 方便快速讀取, 同時在對磁盤文件修改之前進(jìn)行緩存
  • 重做日志(redo log)緩沖


    image.png

    后臺線程的主要作用是負(fù)責(zé)刷新內(nèi)存池中的數(shù)據(jù),保證緩沖池中的內(nèi)存緩存的是最新數(shù)據(jù);將已修改數(shù)據(jù)文件刷新到磁盤文件;保證數(shù)據(jù)庫發(fā)生異常時 InnoDB 能恢復(fù)到正常運(yùn)行 的狀態(tài)

后臺線程

InnoDB 使用的是多線程模型, 其后臺有多個不同的線程負(fù)責(zé)處理不同的任務(wù)

1. Master Thread

這是最核心的一個線程,主要負(fù)責(zé)將緩沖池中的數(shù)據(jù)異步刷新到磁盤,保證數(shù)據(jù)的一致性,包括贓頁的刷新、合并插入緩沖、UNDO 頁的回收等.

2. IO Thread

在 InnoDB 存儲引擎中大量使用了異步 IO 來處理寫 IO 請求, IO Thread 的工作主要是負(fù)責(zé)這些 IO 請求的回調(diào).
可以通過命令來觀察 InnoDB 中的 IO Thread:
SHOW ENGINE INNODB STATUS\G
可以看到, InnoDB 共有10個 IO Thread, 分別是 4個 write、4個 read、1個 insert buffer和1個 log thread.

3. Purge Thread

事物被提交之后, undo log 可能不再需要,因此需要 Purge Thread 來回收已經(jīng)使用比分配的 undo頁. InnoDB 支持多個 Purge Thread, 這樣做可以加快 undo 頁的回收
InnoDB 引擎默認(rèn)設(shè)置為4個 Purge Thread:
SHOW VARIABLES LIKE "innodb_purge_threads"\G

4. Page Cleaner Thread

Page Cleaner Thread 是新引入的,其作用是將之前版本中臟頁的刷新操作都放入單獨(dú)的線程中來完成,這樣減輕了 Master Thread 的工作及對于用戶查詢線程的阻塞

內(nèi)存

1. 緩沖池

緩沖池簡單來說就是一塊內(nèi)存區(qū)域.在數(shù)據(jù)庫中進(jìn)行讀取頁的操作,首先將從磁盤讀到的頁存放在緩沖池中,下一次讀取相同的頁時,首先判斷該頁是不是在緩沖池中,若在,稱該頁在緩沖池中被命中,直接讀取該頁.否則,讀取磁盤上的頁.
對于數(shù)據(jù)庫中頁的修改操作,首先修改在緩沖池中頁,然后再以一定的頻率刷新到磁盤,并不是每次頁發(fā)生改變就刷新回磁盤.
緩沖池的大小直接影響數(shù)據(jù)庫的整體性能,對于 InnoDB 存儲引擎而言,緩沖池配置通過參數(shù) innodb_buffer_pool_size來設(shè)置. 下面顯示本機(jī)虛擬機(jī)上一臺 MySQL 數(shù)據(jù)庫配置:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'\G

緩沖池中緩存的數(shù)據(jù)頁類型有:索引頁、數(shù)據(jù)頁、 undo 頁、插入緩沖、自適應(yīng)哈希索引、 InnoDB 的鎖信息、數(shù)據(jù)字典信息等.索引頁和數(shù)據(jù)頁占緩沖池的很大一部分.
下圖顯示 InnoDB 存儲引擎總內(nèi)存的結(jié)構(gòu)情況.


image.png

2. 重做日志緩沖

InnoDB 存儲引擎先將重做日志信息放入這個緩沖區(qū),然后以一定頻率將其刷新到重做日志文件.重做日志文件一般不需要設(shè)置得很大,因為在下列三種情況下重做日志緩沖中的內(nèi)容會刷新到磁盤的重做日志文件中.

  1. Master Thread 每一秒將重做日志緩沖刷新到重做日志文件
  2. 每個事物提交時會將重做日志緩沖刷新到重做日志文件
  3. 當(dāng)重做日志緩沖剩余空間小于1/2時,重做日志緩沖刷新到重做日志文件

3. 額外的內(nèi)存池

在 InnoDB 存儲引擎中, 對一些數(shù)據(jù)結(jié)構(gòu)本身的內(nèi)存進(jìn)行分配時,需要從額外的內(nèi)存池中進(jìn)行申請.例如,分配了緩沖池,但是每個緩沖池中的幀緩沖還有對應(yīng)的緩沖控制對象,這些對象記錄以一些諸如 LRU, 鎖,等待等信息,而這個對象的內(nèi)存需要從額外的內(nèi)存池中申請.

Checkpoint技術(shù)

1,checkpoint產(chǎn)生的背景
數(shù)據(jù)庫在發(fā)生增刪查改操作的時候,都是先在buffer pool中完成的,為了提高事物操作的效率,buffer pool中修改之后的數(shù)據(jù),并沒有立即寫入到磁盤,這有可能會導(dǎo)致內(nèi)存中數(shù)據(jù)與磁盤中的數(shù)據(jù)產(chǎn)生不一致的情況。
事物要求之一是持久性(Durability),buffer pool與磁盤數(shù)據(jù)的不一致性的情況下發(fā)生故障,可能會導(dǎo)致數(shù)據(jù)無法持久化。
為了防止在內(nèi)存中修改但尚未寫入到磁盤的數(shù)據(jù),在發(fā)生故障重啟數(shù)據(jù)之后產(chǎn)生事物未持久化的情況,是通過日志(redo log)先行的方式來保證的。
redo log可以在故障重啟之后實現(xiàn)“重做”,保證了事物的持久化的特性,但是redo log空間不可能無限制擴(kuò)大,對于內(nèi)存中已修改但尚未提交到磁盤的數(shù)據(jù),也即臟頁,也需要寫入磁盤。
對于內(nèi)存中的臟頁,什么時候,什么情況下,將多少臟頁寫入磁盤,是由多方面因素決定的。
checkpoint的工作之一,就是對于內(nèi)存中的臟頁,在一定條件下將臟頁刷新到磁盤。

2,checkpoint的分類
按照checkpoint刷新的方式,MySQL中的checkpoint分為兩種,也即sharp checkpoint和fuzzy checkpoint。
sharp checkpoint:在關(guān)閉數(shù)據(jù)庫的時候,將buffer pool中的臟頁全部刷新到磁盤中。
fuzzy checkpoint:數(shù)據(jù)庫正常運(yùn)行時,在不同的時機(jī),將部分臟頁寫入磁盤,進(jìn)刷新部分臟頁到磁盤,也是為了避免一次刷新全部的臟頁造成的性能問題。

Innodb關(guān)鍵特性

插入緩沖

當(dāng)插入數(shù)據(jù)需要更新非聚集索引時,如果每次都更新則需要進(jìn)行多次隨機(jī)IO,因此將這些值寫入緩沖對相同頁的進(jìn)行合并提高IO性能。
插入非聚集索引時,先判斷該索引頁是否在緩沖池中,在則直接插入。否則寫入到Insert Buffer對象。
條件:二級索引,索引不能是unique(因為如果是unique則必須保證唯一性,此時得檢查所有索引頁,還是隨機(jī)IO了)
Change Buffer:包括Insert Buffer、Delete Buffer、Purge Buffer,update操作包括將記錄標(biāo)記為已刪除和真正將記錄刪除兩個過程,對應(yīng)后兩個Buffer。
Insert Buffer內(nèi)部是一顆B+樹
Merge Insert Buffer三種情況:
對應(yīng)的索引頁被讀入緩沖池。
對應(yīng)的索引頁的可用空間小于1/32,則強(qiáng)制進(jìn)行合并。
Master Thread中的合并插入緩沖。

兩次寫

在對臟頁刷新到磁盤時,如果某一頁還沒寫完就宕機(jī),此時該頁數(shù)據(jù)已經(jīng)混亂無法通過redo實現(xiàn)恢復(fù)。innodb提供了doublewrite機(jī)制,其刷新臟頁步驟如下:

  1. 先將臟頁數(shù)據(jù)復(fù)制到doublewrite buffer中(2MB內(nèi)存)
  2. 將doublewrite buffer分兩次,每次1MB寫入到doublewrite磁盤(2MB)中。
  3. 馬上同步臟頁數(shù)據(jù)到磁盤。對于數(shù)據(jù)混亂的頁則可以從doublewrite中讀取到,該頁寫到共享表空間。

自適應(yīng)哈希索引

InnoDB存儲引擎會監(jiān)控對表上索引的查找,如果觀察到建立哈希索引可以帶來速度的提升,則建立哈希索引,所以稱之為自適應(yīng)(adaptive) 的。自適應(yīng)哈希索引通過緩沖池的B+樹構(gòu)造而來,因此建立的速度很快。而且不需要將整個表都建哈希索引,InnoDB存儲引擎會自動根據(jù)訪問的頻率和模式 來為某些頁建立哈希索引。

異步IO

linux和windows中提供異步IO,其可以對連續(xù)的頁做合并連續(xù)頁的IO操作使隨機(jī)IO變順序IO。

刷新鄰近頁

刷新頁時判斷相鄰頁是否也是臟頁。


一, InnoDB是什么
1、創(chuàng)始人為Heikki Tuuri(1964,芬蘭赫爾辛基),由Innobase Oy公司開發(fā),mysql5.5版本開始是默認(rèn)的表存儲引擎。
2.特點(diǎn)是行鎖設(shè)計、支持MVCC、支持外鍵、提供一致性非鎖定讀,最有效的使用內(nèi)存和CPU.
3.已應(yīng)用于各大型網(wǎng)站,如:Google、Yahoo!、Facebook等。

二、InnoDB版本
1.早期版本隨mysql數(shù)據(jù)庫版本更新而更新
2.mysql5.1以后,支持存儲引擎開發(fā)商以動態(tài)的形式加載。所以支持兩個版本:
一個是靜態(tài)編譯的innoDB版本(老版本)【支持ACID、行鎖設(shè)計、MVCC】
另一個是動態(tài)加載的InnoDB版本,即InnoDB Plugin,視為InnoDB 1.0.x【增加了compress和dynamic頁格式】
3.mysql 5.5,innoDB升級為1.1.x【增加了Linux AIO、多回滾段】
4.mysql 5.6InnoDB升級為1.2.x【增加了全文索引支持,在線索引添加】
三、InnoDB的體系結(jié)構(gòu)

image

由圖可見,InnoDB有多個內(nèi)存塊,可以認(rèn)為這些內(nèi)存塊組成了一個大的內(nèi)存池,負(fù)責(zé)如下工作:維護(hù)所有進(jìn)程/線程需要訪問的多個內(nèi)部數(shù)據(jù)結(jié)構(gòu);緩存磁盤上的數(shù)據(jù),方便快速的讀取,同事在對磁盤文件的數(shù)據(jù)修改之前在這里緩存;重做日志(redo log)緩沖等
后臺線程的主要作用是負(fù)責(zé)刷新內(nèi)存池中的數(shù)據(jù),保證緩沖池中的內(nèi)存緩存的是最近的數(shù)據(jù)。此外將已修改的數(shù)據(jù)文件刷新到磁盤文件,同事保證在數(shù)據(jù)庫發(fā)生異常的情況下InnoDB能恢復(fù)到正常運(yùn)行狀態(tài)。

第3章 文件

都有什么文件

當(dāng)MySQL實例啟動時,MySQL會先去讀一個配置參數(shù)文件,用來尋找數(shù)據(jù)庫的各種文件所在位置以及指定某些初始化參數(shù),這些參數(shù)通常定義了某種內(nèi)存結(jié)構(gòu)有多大等設(shè)置。默認(rèn)情況下,MySQL實例會按照一定的次序去取,你只需通過命令mysql --help|grep my.cnf來尋找即可。

MySQL參數(shù)文件的作用和Oracle的參數(shù)文件極其類似;不同的是,Oracle實例啟動時若找不到參數(shù)文件,是不能進(jìn)行裝載(mount)操作的。MySQL稍微有所不同,MySQL實例可以不需要參數(shù)文件。

和Oracle參數(shù)文件不同的是,Oracle的參數(shù)文件分為二進(jìn)制的參數(shù)文件(spfile)和文本類型的參數(shù)文件(init.ora),而MySQL的參數(shù)文件僅是文本的,方便的是,你可以通過一些常用的編輯軟件(如vi和emacs)進(jìn)行參數(shù)的編輯。

可以把數(shù)據(jù)庫參數(shù)看成一個鍵/值對??梢酝ㄟ^show variables查看所有的參數(shù),或通過like來過濾參數(shù)名。
從MySQL 5.1版本開始,可以通過information_schema架構(gòu)下的GLOBAL_VARIABLES視圖來進(jìn)行查找,如下所示:
select * from GLOBAL_VARIABLES where VARIABLE_NAME like 'innodb_buffer%'\G;
show variables like 'innodb_buffer%'\G

參數(shù)類型

MySQL參數(shù)文件中的參數(shù)可以分為兩類:動態(tài)(dynamic)參數(shù)和靜態(tài)(static)參數(shù)。動態(tài)參數(shù)意味著你可以在MySQL實例運(yùn)行中進(jìn)行更改;靜態(tài)參數(shù)說明在整個實例生命周期內(nèi)都不得進(jìn)行更改,就好像是只讀(read only)的。
可以通過SET命令對動態(tài)的參數(shù)值進(jìn)行修改,SET的語法如下:
SET
  | [global|session] system_var_name=expr
  | [@@global.|@@session.|@@] system_var_name=expr
這里可以看到global和session關(guān)鍵字,它們表明該參數(shù)的修改是基于當(dāng)前會話還是整個實例的生命周期。
有些動態(tài)參數(shù)只能在會話中進(jìn)行修改,如autocommit;
有些參數(shù)修改完后,在整個實例生命周期中都會生效,如binlog_cache_size;
而有些參數(shù)既可以在會話又可以在整個實例的生命周期內(nèi)生效,如read_buffer_size。
例如: set read_buffer_size=524288;

日志文件:

日志文件類型


image.png

MySQL有幾個不同的日志文件,可以幫助你找出mysqld內(nèi)部發(fā)生的事情:
日志文件: 記入文件中的信息類型
錯誤日志:記錄啟動、運(yùn)行或停止mysqld時出現(xiàn)的問題。
查詢?nèi)罩荆河涗浗⒌目蛻舳诉B接和執(zhí)行的語句。
更新日志:記錄更改數(shù)據(jù)的語句。不贊成使用該日志。
二進(jìn)制日志:記錄所有更改數(shù)據(jù)的語句。還用于復(fù)制。
慢查詢?nèi)罩荆河涗浰袌?zhí)行時間超過long_query_time秒的所有查詢或不使用索引的查詢。

mysql 日志包括:錯誤日志,二進(jìn)制日志,通用查詢?nèi)罩?,慢日志?br> 一:通用查詢?nèi)罩荆?br> 記錄建立的客戶端連接和執(zhí)行的語句
1)show variables like '%verision%';
顯示數(shù)據(jù)庫版本號,存儲引擎等信息
2)查看當(dāng)前的通用日志是否開啟
show variables like '%general%';
開啟通用日志查詢: set global general_log = on;
關(guān)閉通用日志查詢:set global general_log = off;

3)查看當(dāng)前慢文件的格式
show variables like '%log_output%';
設(shè)置通用日志輸出為表和文件方式:
set global log_output = 'file,table';

二:慢查詢?nèi)罩荆?br> 記錄所有執(zhí)行時間超過long_query_time秒的所有查詢或者不適用索引的查詢
默認(rèn)情況下,MySQL不開啟慢查詢?nèi)罩?,long_query_time的默認(rèn)值為10,即運(yùn)行時間超過10s的語句是慢查詢語句。
一般來說,慢查詢發(fā)生在大表中,且查詢的字段沒有建立索引,此時,要匹配查詢的字段會對全表進(jìn)行掃描,耗時查long_query_time表
查看當(dāng)前慢查詢?nèi)罩镜拈_啟情況:
show variables like '%query%';
查詢當(dāng)前慢查詢的語句個數(shù):
show global status like '%slow%';
可以通過查詢語句查看慢查詢的語句:
select * from mysql.slow_log;
三:錯誤日志
MySQL錯誤日志是記錄MySQL運(yùn)行過程中較為嚴(yán)重的警告和錯誤信息,以及MySQL每次啟動和關(guān)閉的詳細(xì)信息。錯誤日志的命名通常為 服務(wù)器主機(jī)名.err
查看錯誤日志的詳細(xì)信息:
show variables like '%log_err%';
錯誤日志歸檔,備份錯誤日志
shell>mv host_name.err host_name.err-old
shell> mysqladmin -u root -p flush-logs
shell>mv host_name.err-old back-directory

四:二進(jìn)制日志
包含了所有更新了的數(shù)據(jù)或者潛在更新了的數(shù)據(jù),
包含關(guān)于每個更新數(shù)據(jù)庫的語句的執(zhí)行時間信息
目的:
盡可能將數(shù)據(jù)庫恢復(fù)到事故故障點(diǎn),因為二進(jìn)制日志包含備份后進(jìn)行的所有更新,用于在主復(fù)制服務(wù)器上記錄所有將發(fā)生送給從服務(wù)器的語句
刪除所有二進(jìn)制文件:
reset master
刪除部分二進(jìn)制文件:
purge master logs
查看是否啟用二進(jìn)制日志:
show variables like '%log_bin%';
查看所有的二進(jìn)制參數(shù)
show variables like '%binlog%';

3.socket文件:一般在/tmp目錄下,名為mysql.sock.
mysql有兩種連接方式,常用的一般是tcp
mysql -h(ip) -uroot -pxxx #常用的
mysql -S /tmp/mysqld.sock
mysql 采用unix socket連接方式,比用tcp的方式更快,但只適用于mysql和應(yīng)用同在一臺PC上。如果不在同一臺pc上,就沒有辦法連接了。

直接就用tcp方式mysql -h localhost -u root -proot直接登錄了,沒有用套接字這方式。由此也可看出不是必須。

Unix系統(tǒng)下本地連接mysql可以采用Unix域套接字方式,這種方式需要一個套接字文件。套接字文件由參數(shù)socket控制。一般在/tmp目錄下,名為mysql.sock /tmp/mysq.sock
SHOW VARIABLES LOKE 'socket'\G

4.pid文件:實例的進(jìn)程文件
mysql啟動時,會將自己的進(jìn)程ID寫入一個文件中,該文件為pid文件。由參數(shù)pid_file控制,默認(rèn)位于數(shù)據(jù)庫目錄下,文件名為主機(jī)名.pid
實驗結(jié)果如下:
5.mysql表結(jié)構(gòu)文件:
每個表或視圖,都有一個以frm為后綴名的文本文件,記錄該表的表結(jié)構(gòu)定義。
6.InnoDB存儲引擎文件
MySql中每個表存儲引擎都有自己獨(dú)有的文件,InnoDB存儲引擎相關(guān)的文件主要包括:重做日志文件,表空間文件。

1.表空間文件

InnoDB采用將存儲的數(shù)據(jù)按表空間(tablespace)進(jìn)行存放的設(shè)計。在默認(rèn)配置下會有一個初始大小為10MB,名為ibdata1的文件。該文件就是默認(rèn)的表空間文件(tablespace file),用戶可以通過參數(shù)innodb_data_file_path對其進(jìn)行設(shè)置.

mysql> show variables like 'innodb_data_file_path';

2.重做日志文件

在默認(rèn)情況下,在InnoDB存儲引擎的數(shù)據(jù)目錄下會有兩個名為ib_logfile0和ib_logfile1的文件。在MySql官方手冊中將其稱為InnoDB存儲引擎的日志文件,不過更準(zhǔn)確的定義應(yīng)該是重做日志文件(redo log file)。
重做日志文件對于InnoDB存儲引擎很重要,它們記錄了對于InnoDB存儲引擎的事務(wù)日志。
當(dāng)實例或介質(zhì) failure時,重做日志文件就派上用場了。例如,數(shù)據(jù)庫由于所在主機(jī)掉電導(dǎo)致實例失敗,InnoDB存儲引擎會使用重做日志恢復(fù)到掉電前的時刻,以此來保證數(shù)據(jù)的完整性。
每個InnoDB存儲引擎至少有1個重做日志文件組,每個文件組下至少有2個重做日志文件,如默認(rèn)的ib_logfile0和ib_logfile1。

第4章 表

4.1 索引組織表

在InnoDB存儲引擎中,因為表都是按照主鍵的順序進(jìn)行存放的,這種存放方式,我們成為索引組織表(IOT)
那么,在整個創(chuàng)建表的過程中,InnoDB是怎么去創(chuàng)建主鍵的
1.顯式的創(chuàng)建主鍵Praimary key
2.判斷表中是否有非空唯一索引,如果有,則為主鍵
3.如果都不符合上述1/2的條件,則會生成UUID的一個隱式主鍵(6字節(jié)大)
在創(chuàng)建的的過程中,如果表中,有多個非空唯一索引的時候,則按照創(chuàng)建索引的順序,以第一個非空唯一索引為準(zhǔn)來創(chuàng)建。

SELECT a,b,c,d,_rowid FROM test;
如果表中有多個非空唯一索引時,InnoDB將選擇建表時第一個定義的非空唯一索引為主鍵,通過_rowid可以顯示表的主鍵,但是只能查看單個列作為主鍵的情況,對于多列組成的主鍵則不可以。

4.2InnoDB邏輯存儲結(jié)構(gòu)

從InnoDB存儲引擎的邏輯存儲結(jié)構(gòu)看,所有數(shù)據(jù)都被邏輯地存放在一個空間中,稱之為表空間(tablespace)。表空間又由段(segment)、區(qū)(extent)、頁(page)組成。頁在一些文檔中有時也稱為(block),InnoDB存儲引擎的邏輯存儲結(jié)構(gòu)大致如圖:


image.png

表空間

表空間可以看做是InnoDB存儲引擎邏輯結(jié)構(gòu)的最高層,所有的數(shù)據(jù)都存放在表空間中。默認(rèn)情況下InnoDB存儲引擎有一個共享表空間ibdata1,即所有數(shù)據(jù)都存放在這個表空間內(nèi)。如果用戶啟動了innodb_file_per_table,則每個表內(nèi)的數(shù)據(jù)可以單獨(dú)放到一個表空間內(nèi),但要注意的是每張表的表空間內(nèi)存放的只是數(shù)據(jù)、索引和插入緩存Bitmap頁,而其他類的數(shù)據(jù),如回滾(undo)信息,插入緩存索引頁、系統(tǒng)事務(wù)信息、二次寫緩存(Double write buffer)等還是存放在原來的共享表空間內(nèi)。
即使設(shè)置了innodb_file_per_table為ON了,共享表空間還是會不斷地增加其大小。

表空間由各個段組成,比如數(shù)據(jù)段,索引段,回滾段等。

區(qū)

區(qū)由連續(xù)的頁組成,在任何情況下區(qū)的大小都是1M。InnoDB存儲引擎一次從磁盤申請大概4-5個區(qū)。在默認(rèn)情況下,頁的大小為16KB,即一個區(qū)中有大概64個連續(xù)的頁。

InnoDB磁盤管理的最小單位。

  • B樹節(jié)點(diǎn)= 一個物理Page(16K)
  • 數(shù)據(jù)按16KB切片為Page 并編號
  • 編號可映射到物理文件偏移(16K * N)
  • B+樹葉子節(jié)點(diǎn)前后形成雙向鏈表
    image
  • 增刪改查之后
    • 有效Node組成雙向鏈表
    • 中間存在空洞
    • 全表掃描時IO可能不連續(xù)
      關(guān)于頁的詳細(xì)結(jié)構(gòu),參考:MySQL的InnoDB索引原理詳解

數(shù)據(jù)是按行進(jìn)行存放的。

行記錄格式

InnoDB 1.0.x之前:

  • Compact
  • Redundant 為了兼容之前的版本

InnoDB 1.0.x之后

  • Compressed
  • Dynamic

查看行格式的方法,注意row_format字段。
show table status like 'table_name'\G

4.3InnoDB行記錄格式

Compact行記錄格式

Compact行設(shè)計目標(biāo)是能高效存放數(shù)據(jù)。簡單來說,如果一個頁中存放的行數(shù)據(jù)越多,其性能就越高。

變長字段長度列表 NULL標(biāo)志位 記錄頭信息 列1數(shù)據(jù) 列2數(shù)據(jù) ……

Redundant行記錄格式

Redundant是Mysql5.0之前的,不過多介紹。

行溢出數(shù)據(jù)

行溢出數(shù)據(jù)
InnoDB存儲引擎可以將一條記錄中的某些數(shù)據(jù)存儲在真正的數(shù)據(jù)頁面之外,即作為行溢出數(shù)據(jù)。一般認(rèn)為BLOB、LOB這類的大對象列類型的存儲會把數(shù)據(jù)存放在數(shù)據(jù)頁面之外。但是,這個理解有點(diǎn)偏差,BLOB可以不將數(shù)據(jù)放在溢出頁面,而即使是varchar列數(shù)據(jù)類型,依然有可能存放為行溢出數(shù)據(jù)。
MySQL數(shù)據(jù)庫的varchar字段,它可以存放65536字節(jié)的數(shù)據(jù),比oracle和sqlserver大多了,但是在使用varchar時也有幾點(diǎn)要注意;

  • 1、65536只是這么說,要建表的時候指定一個字段65536仍然會報錯,因為字段本身還有其他開銷,實際只能存放65532字節(jié)。
  • 2、65532字節(jié)并不是每個varchar字段都可以設(shè)置的,他是一個總和,也就是說如果有2個varchar字段的表,那么每個varchar字段只能設(shè)置65532/2的值。
  • 3、建表時要注意編碼格式哦,varchar(65532)代表的是字節(jié)數(shù),如果使用GBK或者UTF-8那就無法建立成功了,因為GBK一個字符占用2字節(jié),UTF-8一個字符占用3字節(jié)
    頁(PAGE)是innoDB存儲引擎的最小存儲單位,默認(rèn)大小為16KB,及16384字節(jié),行數(shù)據(jù)存儲在頁中;那么一行數(shù)據(jù)如果超過了一個頁能夠存儲的大小怎么辦?比如上面說的varchar(65532),65532字節(jié)該如何存儲?這個時候就會發(fā)生行溢出。

行溢出:
InnoDB存儲引擎可以將一條記錄中的某些數(shù)據(jù)存儲在真正的數(shù)據(jù)頁面之外,一般為BLOB\LOB這類的大對象列類型。但是也不是絕對,BLOB可以不將數(shù)據(jù)放在溢出頁面,而且即便是VARCHAR列數(shù)據(jù)類型,依然有可能被存放為行溢出數(shù)據(jù)

Compressed和Dynamic行記錄格式

InnoDB Plugin引入了新的文件格式(file format,可以理解為新的頁格式),對于以前支持的Compact和Redundant格式將其稱為Antelope文件格式,新的文件格式稱為Barracuda。Barracuda文件格式下?lián)碛袃煞N新的行記錄格式Compressed和Dynamic兩種。新的兩種格式對于存放BLOB的數(shù)據(jù)采用了完全的行溢出的方式,在數(shù)據(jù)頁中只存放20個字節(jié)的指針,實際的數(shù)據(jù)都存放在BLOB Page中,而之前的Compact和Redundant兩種格式會存放768個前綴字節(jié)。
下圖是Barracuda文件格式的溢出行:


image

Compressed行記錄格式的另一個功能就是,存儲在其中的行數(shù)據(jù)會以zlib的算法進(jìn)行壓縮,因此對于BLOB、TEXT、VARCHAR這類大長度類型的數(shù)據(jù)能進(jìn)行非常有效的存儲。

char的行結(jié)構(gòu)存儲

在InnoDB引擎內(nèi)部對于char類型在多字節(jié)字符集類型(如utf8)的存儲,char很明確的被視為了變長類型,對于未能占滿長度的字符還是填充長度。可以說,在多字節(jié)字符集的情況下,char和varchar的行存儲基本是沒有區(qū)別的。

4.4InnoDB數(shù)據(jù)頁結(jié)構(gòu)

InnoDB數(shù)據(jù)頁由以下七個部分組成:

File Header Page Header Infimum+Supremum Records User Records Free Space Page Directory File Trailer

頁是InnoDB存儲引擎管理數(shù)據(jù)庫的最小磁盤單位。頁類型為B-tree node的頁,存放的即是表中行的實際數(shù)據(jù)了。
InnoDB數(shù)據(jù)頁由以下七個部分組成,如圖所示:

  1. File Header(文件頭)。
  2. Page Header(頁頭)。
  3. Infimun+Supremum Records。
  4. User Records(用戶記錄,即行記錄)。
  5. Free Space(空閑空間)。
  6. Page Directory(頁目錄)。
  7. File Trailer(文件結(jié)尾信息)。
    !](http://upload-images.jianshu.io/upload_images/2830277-3092659511a48a88.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    File Header、Page Header、File Trailer的大小是固定的,用來標(biāo)示該頁的一些信息,如Checksum、數(shù)據(jù)所在索引層等。其余部分為實際的行記錄存儲空間,因此大小是動態(tài)的。

File Header

File Header用來記錄頁的一些頭信息。

Page Header

接著File Header部分的是Page Header,用來記錄數(shù)據(jù)頁的狀態(tài)信息

Infimum和Supremum記錄

在InnoDB存儲引擎中,每個數(shù)據(jù)頁中有兩個虛擬的行記錄,用來限定記錄的邊界。Supremum和Infimum分別是主鍵值得上界和下界,這兩個值在頁創(chuàng)建時被建立,并且在任何情況下不會被刪除。

User Records與FreeSpace

User Records即實際存儲行記錄的內(nèi)容。再次強(qiáng)調(diào),InnoDB存儲引擎表總是B+樹索引組織的。
Free Space指的就是空閑空間,同樣也是個鏈表數(shù)據(jù)結(jié)構(gòu)。當(dāng)一條記錄被刪除后,該空間會被加入空閑鏈表中。

Page Directory

Page Directory(頁目錄)中存放了記錄的相對位置。需要記住,B+樹索引本身并不能找到具體的一條記錄,B+樹索引能找到的只是該記錄所在的頁。數(shù)據(jù)庫把頁載入內(nèi)存,然后通過Page Directory再進(jìn)行二分查找。

File Trailer

用來保證頁能夠完整的寫入磁盤,來作比較以此來保證頁的完整性。

Named File Formats機(jī)制

InnoDB存儲引擎通過Named File Formats機(jī)制來解決不同版本下頁結(jié)構(gòu)兼容性的問題。

4.6約束

數(shù)據(jù)完整性

完整性是指數(shù)據(jù)的準(zhǔn)確性和一致性,而完整性檢查就是指檢查數(shù)據(jù)的準(zhǔn)確性和一致性。mysql數(shù)據(jù)庫管理系統(tǒng)提供了一直機(jī)制來檢查數(shù)據(jù)庫中的數(shù)據(jù)是否滿足規(guī)定的條件。以保證數(shù)據(jù)庫中數(shù)據(jù)的準(zhǔn)確性和一致性,這種機(jī)制就是約束。

  • mysql所支持的完整性約束
    NOT NULL(NK)
    DEFAULT
    UNIQUE KEY(UK)約束字段的值是唯一的
    PRIMATYKEY(PK)
    AUTO_INCREMENT
    FOREIGN KEY(FK)

束和索引的區(qū)別

mysql 約束和索引
相同點(diǎn): 保證證數(shù)據(jù)的完整性
區(qū)別: 索引是從數(shù)據(jù)結(jié)構(gòu)的角度來保證數(shù)據(jù)完整性, 而 約束是一個邏輯概念,用來保證數(shù)據(jù)完整性.

保證數(shù)據(jù)完整性的方法:

一. 對錯誤數(shù)據(jù)的約束
設(shè)置sql_mode 為 嚴(yán)格模式, 來提示報錯而不是警告
服務(wù)器配置: my.cnf sql_mode ='STRICT_TRANS_TABLES' ;
客戶端 使用 : set sql_mode = 'STRICT_TRANS_TABLES' ;
比如在非嚴(yán)格模式下, 對字段已設(shè)置為not null , 插入了 非法日期的值: 比如 2009-02-30.

二. ENUM 和 SET 約束(針對數(shù)據(jù)類型).
比如假設(shè) 性別 只允許兩種, male, female. enum('male', 'female'), 也可以 enum(0, 1). 如果設(shè)置為 tinyint(1), 值就可以存在 0-9.

三. 觸發(fā)器約束
通過創(chuàng)建觸發(fā)器來過濾錯誤的數(shù)據(jù).一般用在字讀間的計算.

四.外鍵
InnoDB支持外鍵, 可以通過外鍵來保證數(shù)據(jù)的完整性. 比如 一個用戶表, 對應(yīng)有張子表來存儲一些額外的信息,
子表通過外鍵就可以達(dá)到只要主表有 delete 和 update 的操作,對應(yīng)的數(shù)據(jù)也會 delete 和 update.
MyISAM 表不支持外鍵,可以只用 觸發(fā)器來控制數(shù)據(jù)完整性.

4.7 視圖

視圖的主要用途之一是被用作一個抽象裝置,特別是對于一些應(yīng)用程序,程序本身不需要關(guān)心基表的結(jié)構(gòu),只需要按照視圖定義來獲取數(shù)據(jù)或者更新數(shù)據(jù)。因此,視圖同時在一定程度上起到一個安全層的作用。
Mysql本身不支持物化視圖,需要采用SELECT然后INSERT類似這種的方式導(dǎo)入視圖的數(shù)據(jù)到表中。

4.8 分區(qū)

Mysql數(shù)據(jù)庫表分區(qū)深入詳解

0、mysql數(shù)據(jù)庫分區(qū)的由來?

1)傳統(tǒng)不分區(qū)數(shù)據(jù)庫痛點(diǎn)

mysql數(shù)據(jù)庫中的數(shù)據(jù)是以文件的形勢存在磁盤上的,默認(rèn)放在/mysql/data下面(可以通過my.cnf中的datadir來查看),
一張表主要對應(yīng)著三個文件,一個是frm存放表結(jié)構(gòu)的,一個是myd存放表數(shù)據(jù)的,一個是myi存表索引的。

2)數(shù)據(jù)庫分區(qū)處理

如果一張表的數(shù)據(jù)量太大的話,那么myd,myi就會變的很大,查找數(shù)據(jù)就會變的很慢,這個時候我們可以利用mysql的分區(qū)功能,在物理上將這一張表對應(yīng)的三個文件,分割成許多個小塊,這樣呢,我們查找一條數(shù)據(jù)時,就不用全部查找了,只要知道這條數(shù)據(jù)在哪一塊,然后在那一塊找就行了。如果表的數(shù)據(jù)太大,可能一個磁盤放不下,這個時候,我們可以把數(shù)據(jù)分配到不同的磁盤里面去。
表分區(qū)是Mysql被Oracle收購后推出的一個新特性。

一、表分區(qū)通俗解釋

通俗地講表分區(qū)是將一大表,根據(jù)條件分割成若干個小表。mysql5.1開始支持?jǐn)?shù)據(jù)表分區(qū)了。
如:某用戶表的記錄超過了600萬條,那么就可以根據(jù)入庫日期將表分區(qū),也可以根據(jù)所在地將表分區(qū)。當(dāng)然也可根據(jù)其他的條件分區(qū)。

二、為什么要對表進(jìn)行分區(qū)?

為了改善大型表以及具有各種訪問模式的表的可伸縮性,可管理性和提高數(shù)據(jù)庫效率。

2.1 表分區(qū)要解決的問題:

當(dāng)表非常大,或者表中有大量的歷史記錄,而“熱數(shù)據(jù)”卻位于表的末尾。如日志系統(tǒng)、新聞。。此時就可以考慮分區(qū)表?!咀ⅲ捍颂幰部梢允褂梅直?,但是會增加業(yè)務(wù)的復(fù)雜性?!?/p>

2.2 表分區(qū)有如下優(yōu)點(diǎn):

1)與單個磁盤或文件系統(tǒng)分區(qū)相比,可以存儲更多的數(shù)據(jù)。
2)對于那些已經(jīng)失去保存意義的數(shù)據(jù),通??梢酝ㄟ^刪除與那些數(shù)據(jù)有關(guān)的分區(qū),很容易地刪除那些數(shù)據(jù)。
相反地,在某些情況下,添加新數(shù)據(jù)的過程又可以通過為那些新數(shù)據(jù)專門增加一個新的分區(qū),來很方便地實現(xiàn)。
同樣的,你可以很快的通過刪除分區(qū)來移除舊數(shù)據(jù)。你還可以優(yōu)化、檢查、修復(fù)個別分區(qū)。
3)一些查詢可以得到極大的優(yōu)化。 可以把一些歸類的數(shù)據(jù)放在一個分區(qū)中,可以減少服務(wù)器檢查數(shù)據(jù)的數(shù)量加快查詢。
這主要是借助于滿足一個給定WHERE語句的數(shù)據(jù)可以只保存在一個或多個分區(qū)內(nèi),這樣在查找時就不用查找其他剩余的分區(qū)。
PS:因為分區(qū)可以在創(chuàng)建了分區(qū)表后進(jìn)行修改,所以在第一次配置分區(qū)方案時還不曾這么做時,可以重新組織數(shù)據(jù),來提高那些常用查詢的效率。
4)涉及到例如SUM()和COUNT()這樣聚合函數(shù)的查詢,可以很容易地進(jìn)行并行處理。
這種查詢的一個簡單例子如
“SELECT salesperson_id, COUNT (orders) as order_total FROM sales GROUP BY salesperson_id;”。
通過“并行”,這意味著該查詢可以在每個分區(qū)上同時進(jìn)行,最終結(jié)果只需通過總計所有分區(qū)得到的結(jié)果。
5)通過跨多個磁盤來分散數(shù)據(jù)查詢,來獲得更大的查詢吞吐量。

三、mysql分區(qū)類型

根據(jù)所使用的不同分區(qū)規(guī)則可以分成幾大分區(qū)類型。


分區(qū)類型

子分區(qū)
分區(qū)其實是對每個分區(qū)表的每個分區(qū)進(jìn)行再次分隔,目前只有RANGE和LIST分區(qū)的表可以再進(jìn)行子分區(qū),子分區(qū)只能是HASH或者KEY分區(qū)。子分區(qū)可以將原本的數(shù)據(jù)進(jìn)行再次的分區(qū)劃分。

四、常見分區(qū)操作

常見分區(qū)操作

五、獲取分區(qū)表信息的方法

5.1 show create table 表名

可以查看創(chuàng)建分區(qū)表的create語句
舉例:

mysql> show create table foo_list;

5. 2 show table status

可以查看表是不是分區(qū)表
舉例:
SHOW TABLE STATUS LIKE ‘foo_range’;

5.3 查看information_schema.partitions表

如下命令可以查看表具有哪幾個分區(qū)、分區(qū)的方法、分區(qū)中數(shù)據(jù)的記錄數(shù)等信息
mysql> select
-> partition_name part,
-> partition_expression expr,
-> partition_description descr,
-> table_rows
-> from information_schema.partitions where
-> table_schema = schema()
-> and table_name='foo_range';

六、分區(qū)適用場景

7.1常見使用場景

1)當(dāng)數(shù)據(jù)量很大(過T)時,肯定不能把數(shù)據(jù)再如到內(nèi)存中,這樣查詢一個或一定范圍的item是很耗時。另外一般這情況下,歷史數(shù)據(jù)或不常訪問的數(shù)據(jù)占很大部分,最新或熱點(diǎn)數(shù)據(jù)占的比例不是很大。這時可以根據(jù)有些條件進(jìn)行表分區(qū)。
2)分區(qū)表的更易管理,比如刪除過去某一時間的歷史數(shù)據(jù),直接執(zhí)行truncate,或者狠點(diǎn)drop整個分區(qū),這比detele刪除效率更高
3)當(dāng)數(shù)據(jù)量很大,或者將來很大的,但單塊磁盤的容量不夠,或者想提升IO效率的時候,可以把沒分區(qū)中的子分區(qū)掛載到不同的磁盤上。
4)使用分區(qū)表可避免某些特殊的瓶頸,例如Innodb的單個索引的互斥訪問..
5)單個分區(qū)表的備份很恢復(fù)會更有效率,在某些場景下
總結(jié):可伸縮性,可管理性,提高數(shù)據(jù)庫查詢效率。

7.2 業(yè)務(wù)場景舉例

項目中需要動態(tài)新建、刪除分區(qū)。如新聞表,按照時間維度中的月份對其分區(qū),為了防止新聞表過大,只保留最近6個月的分區(qū),同時預(yù)建后面3個月的分區(qū),這個刪除、預(yù)建分區(qū)的過程就是分區(qū)表的動態(tài)管理。

第5章 索引與算法

第6章 鎖

鎖 lock latch

鎖是計算機(jī)協(xié)調(diào)多個進(jìn)程或純線程并發(fā)訪問某一資源的機(jī)制。
鎖定機(jī)制簡單來說,就是數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性,而使各種共享資源在被并發(fā)訪問變得有序所設(shè)計的一種規(guī)則。在數(shù)據(jù)庫中,除傳統(tǒng)的計算資源(CPU、RAM、I/O)的爭用以外,數(shù)據(jù)也是一種供許多用戶共享的資源。如何保證數(shù)據(jù)并發(fā)訪問的一致性、有效性是所在有數(shù)據(jù)庫必須解決的一個問題,鎖沖突也是影響數(shù)據(jù)庫并發(fā)訪問性能的一個重要因素。

相對其他數(shù)據(jù)庫而言,MySQL的鎖機(jī)制比較簡單,其最顯著的特點(diǎn)是不同的存儲引擎支持不同的鎖機(jī)制。

MySQL大致可歸納為以下3種鎖:

  • 表級鎖:開銷小,加鎖快;不會出現(xiàn)死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低。
  • 行級鎖:開銷大,加鎖慢;會出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。
  • 頁面鎖:開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般

lock與latch區(qū)別
這里要區(qū)分鎖中容易令人混淆的概念lock與latch。在數(shù)據(jù)庫中,lock與latch都可以成為鎖,但兩者有截然不同的含義

  • latch 一般稱為閂鎖(輕量級的鎖) 因為其要求鎖定的時間非常短,若遲勛時間長,則應(yīng)用性能非常差,在InnoDB存儲引擎中,latch有可以分為mutex(互斥鎖)和rwlock(讀寫鎖)其目的用來保證并發(fā)線程操作臨界資源的正確性,并且沒有死鎖檢測的機(jī)制
  • lock的對象是事務(wù),用來鎖定的是數(shù)據(jù)庫中的UI想,如表、頁、行。并且一般lock對象僅在事務(wù)commit或rollback后進(jìn)行釋放(不同事務(wù)隔離級別釋放的時間可能不同),此外lock正如大多數(shù)數(shù)據(jù)庫中一樣,是有死鎖機(jī)制的。表顯示了lock與latch的不同


    image

InnoDB引擎中的鎖

InnoDB與MyISAM的最大不同有兩點(diǎn):一是支持事務(wù)(TRANSACTION);二是采用了行級鎖。行級鎖與表級鎖本來就有許多不同之處,另外,事務(wù)的引入也帶來了一些新問題。下面我們先介紹一點(diǎn)背景知識,然后詳細(xì)討論InnoDB的鎖問題。

背景知識

事務(wù)(Transaction)及其ACID屬性
事務(wù)是由一組SQL語句組成的邏輯處理單元,事務(wù)具有以下4個屬性,通常簡稱為事務(wù)的ACID屬性。

  • 原子性(Atomicity):事務(wù)是一個原子操作單元,其對數(shù)據(jù)的修改,要么全都執(zhí)行,要么全都不執(zhí)行。
  • 一致性(Consistent):在事務(wù)開始和完成時,數(shù)據(jù)都必須保持一致狀態(tài)。這意味著所有相關(guān)的數(shù)據(jù)規(guī)則都必須應(yīng)用于事務(wù)的修改,以保持?jǐn)?shù)據(jù)的完整性;事務(wù)結(jié)束時,所有的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(如B樹索引或雙向鏈表)也都必須是正確的。
  • 隔離性(Isolation):數(shù)據(jù)庫系統(tǒng)提供一定的隔離機(jī)制,保證事務(wù)在不受外部并發(fā)操作影響的“獨(dú)立”環(huán)境執(zhí)行。這意味著事務(wù)處理過程中的中間狀態(tài)對外部是不可見的,反之亦然。
  • 持久性(Durable):事務(wù)完成之后,它對于數(shù)據(jù)的修改是永久性的,即使出現(xiàn)系統(tǒng)故障也能夠保持。
      銀行轉(zhuǎn)帳就是事務(wù)的一個典型例子。
并發(fā)事務(wù)處理帶來的問題

相對于串行處理來說,并發(fā)事務(wù)處理能大大增加數(shù)據(jù)庫資源的利用率,提高數(shù)據(jù)庫系統(tǒng)的事務(wù)吞吐量,從而可以支持更多的用戶。但并發(fā)事務(wù)處理也會帶來一些問題,主要包括以下幾種情況。

  • 更新丟失(Lost Update):當(dāng)兩個或多個事務(wù)選擇同一行,然后基于最初選定的值更新該行時,由于每個事務(wù)都不知道其他事務(wù)的存在,就會發(fā)生丟失更新問題--最后的更新覆蓋了由其他事務(wù)所做的更新。例如,兩個編輯人員制作了同一文檔的電子副本。每個編輯人員獨(dú)立地更改其副本,然后保存更改后的副本,這樣就覆蓋了原始文 檔。最后保存其更改副本的編輯人員覆蓋另一個編輯人員所做的更改。如果在一個編輯人員完成并提交事務(wù)之前,另一個編輯人員不能訪問同一文件,則可避免此問 題。
  • 臟讀(Dirty Reads):一個事務(wù)正在對一條記錄做修改,在這個事務(wù)完成并提交前,這條記錄的數(shù)據(jù)就處于不一致狀態(tài);這時,另一個事務(wù)也來讀取同一條記錄,如果不加 控制,第二個事務(wù)讀取了這些“臟”數(shù)據(jù),并據(jù)此做進(jìn)一步的處理,就會產(chǎn)生未提交的數(shù)據(jù)依賴關(guān)系。這種現(xiàn)象被形象地叫做"臟讀"。
  • 不可重復(fù)讀(Non-Repeatable Reads):一個事務(wù)在讀取某些數(shù)據(jù)后的某個時間,再次讀取以前讀過的數(shù)據(jù),卻發(fā)現(xiàn)其讀出的數(shù)據(jù)已經(jīng)發(fā)生了改變、或某些記錄已經(jīng)被刪除了!這種現(xiàn)象就叫做“不可重復(fù)讀”。
  • 幻讀(Phantom Reads):一個事務(wù)按相同的查詢條件重新讀取以前檢索過的數(shù)據(jù),卻發(fā)現(xiàn)其他事務(wù)插入了滿足其查詢條件的新數(shù)據(jù),這種現(xiàn)象就稱為“幻讀”。
事務(wù)隔離級別
  • 在上面講到的并發(fā)事務(wù)處理帶來的問題中,“更新丟失”通常是應(yīng)該完全避免的。但防止更新丟失,并不能單靠數(shù)據(jù)庫事務(wù)控制器來解決,需要應(yīng)用程序?qū)σ碌臄?shù)據(jù)加必要的鎖來解決,因此,防止更新丟失應(yīng)該是應(yīng)用的責(zé)任。
  • “臟讀”、“不可重復(fù)讀”和“幻讀”,其實都是數(shù)據(jù)庫讀一致性問題,必須由數(shù)據(jù)庫提供一定的事務(wù)隔離機(jī)制來解決。
數(shù)據(jù)庫實現(xiàn)事務(wù)隔離的方式,基本上可分為以下兩種。
  • 一種是在讀取數(shù)據(jù)前,對其加鎖,阻止其他事務(wù)對數(shù)據(jù)進(jìn)行修改。
  • 另一種是不用加任何鎖,通過一定機(jī)制生成一個數(shù)據(jù)請求時間點(diǎn)的一致性數(shù)據(jù)快照(Snapshot),并用這個快照來提供一定級別(語句級或事務(wù)級)的一 致 性讀取。從用戶的角度來看,好像是數(shù)據(jù)庫可以提供同一數(shù)據(jù)的多個版本,因此,這種技術(shù)叫做數(shù)據(jù)多版本并發(fā)控制(MultiVersion Concurrency Control,簡稱MVCC或MCC),也經(jīng)常稱為多版本數(shù)據(jù)庫。

數(shù)據(jù)庫的事務(wù)隔離越嚴(yán)格,并發(fā)副作用越 小,但付出的代價也就越大,因為事務(wù)隔離實質(zhì)上就是使事務(wù)在一定程度上 “串行化”進(jìn)行,這顯然與“并發(fā)”是矛盾的。同時,不同的應(yīng)用對讀一致性和事務(wù)隔離程度的要求也是不同的,比如許多應(yīng)用對“不可重復(fù)讀”和“幻讀”并不敏 感,可能更關(guān)心數(shù)據(jù)并發(fā)訪問的能力。

為了解決“隔離”與“并發(fā)”的矛盾,ISO/ANSI SQL92定義了4個事務(wù)隔離級別,每個級別的隔離程度不同,允許出現(xiàn)的副作用也不同,應(yīng)用可以根據(jù)自己的業(yè)務(wù)邏輯要求,通過選擇不同的隔離級別來平衡 “隔離”與“并發(fā)”的矛盾。表20-5很好地概括了這4個隔離級別的特性。


image.png

 最后要說明的是:各具體數(shù)據(jù)庫并不一定完全實現(xiàn)了上述4個隔離級別,例如,Oracle只提供Read committed和Serializable兩個標(biāo)準(zhǔn)隔離級別,另外還提供自己定義的Read only隔離級別;SQL Server除支持上述ISO/ANSI SQL92定義的4個隔離級別外,還支持一個叫做“快照”的隔離級別,但嚴(yán)格來說它是一個用MVCC實現(xiàn)的Serializable隔離級別。MySQL 支持全部4個隔離級別,但在具體實現(xiàn)時,有一些特點(diǎn),比如在一些隔離級別下是采用MVCC一致性讀,但某些情況下又不是

一致性非鎖定讀(consistent nonlocking read)

一致性非鎖定讀是InnoDB存儲引擎通過多版本控制(multi versioning)的方式來讀取當(dāng)前執(zhí)行時間數(shù)據(jù)庫中的數(shù)據(jù)。如果被讀的數(shù)據(jù)行被加了排他鎖,在讀取這行數(shù)據(jù)的時候并不會等待鎖釋放,而是讀取該行的一個快照數(shù)據(jù)。 之所以稱為非鎖定讀,因為不需要等待被訪問行的X鎖的釋放。快照數(shù)據(jù)是指修改行之前的數(shù)據(jù)版本,該實現(xiàn)通過undo段來完成。非鎖定讀的方式極大提高了數(shù)據(jù)庫的并發(fā)性。在InnoDB存儲引擎中,這是默認(rèn)的讀取方式。

一致性鎖定讀

在默認(rèn)情況下,InnoDB存儲引擎對數(shù)據(jù)采用的是一致性非鎖定讀。但是有些情況下為了保證數(shù)據(jù)邏輯的一致性,需要對SELECT的操作加鎖。InnoDB存儲引擎對于SELECT語句支持兩種一致性的鎖定讀(locking read)操作。
1、 SELECT …… FOR UPDATE
2、 SELECT …… LOCK IN SHARE MODE
  其中,SELECT …… FOR UPDATE對讀取的記錄加一個X鎖(排它鎖),其他事務(wù)不能對已鎖定的行加任何鎖。而SELECT …… LOCK IN SHARE MODE是對讀取的記錄加一個S鎖(共享鎖),其他事物可以向被鎖定的行加S鎖,但是如果加X鎖,則會被阻塞。

自增長與鎖

自增長在數(shù)據(jù)庫中是一種非常常見的一種屬性,也是很多DBA或開發(fā)人員或者DBA人員首選的主鍵方式。在InnoDB存儲引擎的內(nèi)存結(jié)構(gòu)中,對每個含有自增長值的表都有一個自增長計數(shù)器(auto_increment counter)。當(dāng)對含有自增長的計數(shù)器的表進(jìn)行插入操作時,這個計數(shù)器會被初始化,執(zhí)行如下的語句來得到計數(shù)器的值:
select max(auto_inc_col) from test for update;
插入操作會根據(jù)這個自增長的計數(shù)器值加1賦予自增長列。這個實現(xiàn)方式稱作為AUTO-INC Locking。這種鎖采用一種特殊的表鎖機(jī)制,為了提高插入的性能,鎖不是在一個事務(wù)完成后才釋放,而是在完成對自增長值插入的SQL語句后立即釋放。

外鍵與鎖

簡單說一下外鍵,外鍵主要用于引用完整性的約束檢查。在InnoDB存儲引擎中,對于一個外鍵列,如果沒有顯示地對這個列加索引,InnoDB存儲引擎會自動對其加一個索引,因為這樣可以避免表鎖。這比Oracle數(shù)據(jù)庫做得好,Oracle數(shù)據(jù)庫不會自動添加索引,用戶必須自己手動添加,這也導(dǎo)致了Oracle數(shù)據(jù)庫中可能產(chǎn)生死鎖。

鎖的算法

阻塞

數(shù)據(jù)庫阻塞的現(xiàn)象:第一個連接占有資源沒有釋放,而第二個連接需要獲取這個資源。如果第一個連接沒有提交或者回滾,第二個連接會一直等待下去,直到第一個連接釋放該資源為止。對于阻塞,數(shù)據(jù)庫無法處理,所以對數(shù)據(jù)庫操作要及時地提交或

者回滾。

死鎖

是指兩個或兩個以上的事務(wù)在執(zhí)行過程中,因爭奪資源而造成的一種互相等待的現(xiàn)象。若無外力作用,事務(wù)都將無法推進(jìn)下去,解決死鎖的最簡單問題是不要有等待,任何的等待都轉(zhuǎn)換為回滾,并且事務(wù)重新開始,但在線上環(huán)境,這可能會導(dǎo)致并發(fā)性能下降,甚至任何一個事務(wù)都不能進(jìn)行,而這所帶來的問題遠(yuǎn)比死鎖的問題更嚴(yán)重

解決死鎖的問題最簡單的一種方法是超時,當(dāng)兩個事務(wù)互相等待時,當(dāng)一個等待時間超過設(shè)置的某一閾值時,其中一個事務(wù)回滾,另一個等待的事務(wù)就能繼續(xù)運(yùn)行了,在InnoDB存儲引擎中,參數(shù)innodb_lock_wait_timeout用來設(shè)置超時時間

超時機(jī)制雖然簡單,但是其僅通過超時后對事務(wù)進(jìn)行回滾的方式處理,或者說其是根據(jù)FIFO的順序選擇回滾對象,但若超時的事務(wù)所占權(quán)重比較大,如事務(wù)操作更新了很多航,占用了較多的undo log,這是采用FIFO方式,就顯得不合適了,因為回滾這個事務(wù)的時間相對另一個事務(wù)所占用的時間可能會更多

除了超時機(jī)制,當(dāng)前的數(shù)據(jù)庫還采用wait-for graph(等待圖)的方式來進(jìn)行死鎖檢測,較之超時的解決方案,這是一種更為主動的死鎖檢測方式。InnoDB存儲引擎也是采用這種方式。wait-for graph要求數(shù)據(jù)庫保存以下兩種信息

  • 鎖的信息鏈表
  • 事務(wù)等待鏈表

鎖升級

第7章 事務(wù)

第8章 備份與恢復(fù)

第9章 性能調(diào)優(yōu)

第10章 InnoDB存儲引擎源代碼的編譯和調(diào)試

目錄


InnoDB存儲引擎是開源的,這意味著你可以獲得其源代碼,并查看內(nèi)部的具體實現(xiàn)。任何時候,WHY都比WHAT重要。通過研究源代碼,可以更好地理解數(shù)據(jù)庫是如何工作的,從而知道如何使數(shù)據(jù)庫更好地為你工作。如果你有一定的編程能力,則完全可以對InnoDB存儲引擎進(jìn)行擴(kuò)展,開發(fā)出新的功能模塊來更好地支持你的數(shù)據(jù)庫應(yīng)用。

回到頂部

獲取InnoDB存儲引擎源代碼

InnoDB存儲引擎的源代碼被包含在MySQL數(shù)據(jù)庫的源代碼中,在MySQL的官方網(wǎng)站鏈接為:http://www.mysql.com/downloads/mysql/。下載MySQL數(shù)據(jù)庫的源代碼即可。 這里有不同操作系統(tǒng)下的源代碼可供下載,一般只需下載Generic Linux的版本即可。通過MySQL官網(wǎng)首頁的Download鏈接,可以迅速地找到GA版本的下載。但是,如果想要下載目前正在開發(fā)的MySQL版本,可能在官網(wǎng)找了很久都找不到鏈接。這時,只要把下載的鏈接從www換到dev即可:如http://dev.mysql.com/downloads/mysql,在這里可以找到開發(fā)中的MySQL版本的源代碼了。單擊“Download”下載標(biāo)簽后可以進(jìn)入下載頁面。當(dāng)然,如果你有mysql.com賬戶,可以進(jìn)行登錄。MySQL官方提供了大量的鏡像用來分流下載,你可以根據(jù)所在的位置選擇下載速度最快的地址,中國用戶一般可以在“Asia”這里的鏡像下載。

下載的文件是tar.gz結(jié)尾的文件,可以通過Linux的tar命令、Windows的WinRAR工具來進(jìn)行解壓,解壓后得到一個文件夾,這里面就包含了MySQL數(shù)據(jù)庫的所有源代碼。所有存儲引擎的源代碼都被放在storage的文件夾下,其源代碼結(jié)構(gòu)如圖所示。

image

可以看到,所有存儲引擎的源代碼都在這里。文件夾名一般就是存儲引擎的名稱,如archive、blackhole、csv、fedorated、heap、ibmdb2i、myisam、innobase。從MySQL 5.5版本開始,InnoDB Plugin已經(jīng)作為默認(rèn)的InnoDB存儲引擎版本;而在MySQL 5.1的源代碼中,應(yīng)該可以看到兩個版本的InnoDB存儲引擎源代碼??梢钥吹接衖nnobase和innodb_plugin兩個文件夾:innobase文件夾是舊的InnoDB存儲引擎的源代碼;innodb_plugin文件夾是InnoDB Plugin存儲引擎的源代碼。如果你想將InnoDB Plugin直接靜態(tài)編譯到MySQL數(shù)據(jù)庫中,那么需要刪除innobase文件夾,再將innodb_plugin文件夾重命名為innobase。

回到頂部

InnoDB源代碼結(jié)構(gòu)

進(jìn)入InnoDB存儲引擎的源代碼文件夾,可以看到源代碼結(jié)構(gòu) :

下面介紹一些主要文件夾內(nèi)源代碼的具體作用:

btr:B+樹的實現(xiàn)。

buf:緩沖池的實現(xiàn),包括LRU算法、Flush刷新算法等。

dict:InnoDB存儲引擎內(nèi)存數(shù)據(jù)字典的實現(xiàn)。

dyn:InnoDB存儲引擎動態(tài)數(shù)組的實現(xiàn)。

fil:InnoDB存儲引擎中文件數(shù)據(jù)結(jié)構(gòu)以及對于文件的一些操作。

fsp:你可以理解為file space,即對InnoDB存儲引擎物理文件的管理,如頁、區(qū)、段等。

ha:哈希算法的實現(xiàn)。

handler:繼承于MySQL的handler,插件式存儲引擎的實現(xiàn)。

ibuf:插入緩沖的實現(xiàn)。

include:InnoDB將頭文件(.h,.ic)都統(tǒng)一放在這個文件夾下。

lock:InnoDB存儲引擎鎖的實現(xiàn),如S鎖、X鎖以及定義鎖的一系列算法。

log:日志緩沖和重組日志文件的實現(xiàn)。對重組日志感興趣的,應(yīng)該好好閱讀該源代碼。

mem:輔助緩沖池的實現(xiàn),用來申請一些數(shù)據(jù)結(jié)構(gòu)的內(nèi)存。

mtr:事務(wù)的底層實現(xiàn)。

os:封裝一些對于操作系統(tǒng)的操作。

page:頁的實現(xiàn)。

row:對于各種類型行數(shù)據(jù)的操作。

srv:對于InnoDB存儲引擎參數(shù)的設(shè)計。

sync:InnoDB存儲引擎互斥量(Mutex)的實現(xiàn)。

thr:InnoDB儲存引擎封裝的可移植的線程庫。

trx:事務(wù)的實現(xiàn)。

ut:工具類。

回到頂部

編譯和調(diào)試InnoDB源代碼

Windows下的調(diào)試

在Windows平臺下,可以通過Visual Studion 2003、2005和2008開發(fā)工具對MySQL的源代碼進(jìn)行編譯和調(diào)試。在此之前,需要預(yù)先安裝如下的工具:

CMake:可以從http://www.cmake.org下載。

bison:可以從http://gnuwin32.sourceforge.net/packages/bison.htm下載。

安裝之后,還需要通過configure.js這個命令進(jìn)行配置:

C:\workdir>win\configure.js options

option比較重要的選項如下所示。

WITH_INNOBASE_STORAGE_ENGINE:支持InnoDB存儲引擎。

WITH_PARTITION_STORAGE_ENGINE:分區(qū)支持。

WITH_ARCHIVE_STORAGE_ENGINE:支持Archive存儲引擎。

WITH_BLACKHOLE_STORAGE_ENGINE:支持Blackhole存儲引擎。

WITH_EXAMPLE_STORAGE_ENGINE:支持Example存儲引擎,這個存儲引擎是展示給開發(fā)人員的,你可以從這個存儲引擎開始構(gòu)建自己的存儲引擎。

WITH_FEDERATED_STORAGE_ENGINE:支持Federated存儲引擎。

WITH_NDBCLUSTER_STORAGE_ENGINE:支持NDB Cluster存儲引擎。

如果只是比較關(guān)心InnoDB存儲引擎,可以這樣進(jìn)行設(shè)置,如圖所示。

image

之后,可以根據(jù)你使用的是Visual Studio 2005還是Visual Studio 2008,在win文件下運(yùn)行build-vsx.bat文件來生成Visual Studio的工程文件。build-vs8.bat表示Visual Studio 2005,build-vs8_x64.bat表示需要編譯64位的MySQL數(shù)據(jù)庫。如我們需要在32位的操作系統(tǒng)下使用Visual Studio 2008進(jìn)行調(diào)試工作,則可以使用如下命令:

D:\Project\mysql-5.5.5-m3>win\build-vs9.bat

這樣就生成了MySQL.sln的工程文件,打開這個工程文件并將mysqld這個項目設(shè)置為默認(rèn)的啟動項,就可以進(jìn)行MySQL的編譯和調(diào)試了。

之后的編譯、斷點(diǎn)的設(shè)置和調(diào)試,與在Visual Studio下操作一般的程序沒有什么區(qū)別。

Linux下的調(diào)試

Linux下的調(diào)試,通常使用Eclipse。其他一些類Unix操作系統(tǒng),如Solaris、FreeBSD、MAC,同樣可以使用Eclipse進(jìn)行調(diào)試。

  1. 到http://www.eclipse.org/downloads/下載并安裝Eclipse IDE for C/C++Developers。
  2. 解壓MySQL源代碼到指定目錄,如解壓到/root/workspace/mysql-5.5.5-m3,
  3. 運(yùn)行如下命令產(chǎn)生Make文件(Eclipse會使用產(chǎn)生的這些Make文件):[root mysql-5.5.5-m3]#BUILD/compile-amd64-debug-max-no-ndb-c,BUILD下有很多compile文件,你可以選擇你所需要的文件。編譯的平臺是64位的Linux系統(tǒng),并且希望可以進(jìn)行Debug調(diào)試,因此選擇了compile-amd64-debug-max-no-ndb文件。注意-c選項,這個選項只生產(chǎn)Make文件,不進(jìn)行編譯。
  4. 接著打開Eclipse,新建一個C++的項目。給項目取個名稱,如這里的項目名為mysql_5_5_5,并選擇一個空的項目。選擇Finish按鈕后,可以看到新產(chǎn)生的一個空項目。
  5. 之后選擇左邊的Project Explorer,右擊項目mysql_5_5_5,選擇新建文件夾,將文件夾/root/workspace/mysql-5.5.5-m3導(dǎo)入工程中。
  6. 導(dǎo)入文件夾后,再右擊項目名mysql_5_5_5,選擇項目屬性,在C/C++Build選項這里進(jìn)行設(shè)置,需要將Build directory選擇為源代碼所在路徑。 編譯配置完后,程序就會自動開始執(zhí)行編譯工作了。
  7. 上述的這個過程只是編譯的過程,換句話說,編譯完后就產(chǎn)生了mysqld這樣的執(zhí)行文件。如果想要進(jìn)行調(diào)試,還需要在Debug這里進(jìn)行如下的配置。 另外如果需要配置一些額外的參數(shù),需要切換到Arguments選項。
  8. 之后就可以設(shè)置斷點(diǎn),進(jìn)行調(diào)試工作了,這和一般的程序并沒有什么不同。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容