觀其大綱
第1章 MySQL體系結構和存儲引擎
第2章 InnoDB存儲引擎
第3章 文件
第4章 表
第5章 索引與算法
第6章 鎖
第7章 事務
第8章 備份與恢復
第9章 性能調優
第10章 InnoDB存儲引擎源代碼的編譯和調試
大綱細節
第1章 MySQL體系結構和存儲引擎 1
1.1 定義數據庫和實例 1
1.2 MySQL體系結構 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 命名管道和共享內存 15
1.5.3 UNIX域套接字 15
1.6 小結 15
第2章 InnoDB存儲引擎 17
2.1 InnoDB存儲引擎概述 17
2.2 InnoDB存儲引擎的版本 18
2.3 InnoDB體系架構 19
2.3.1 后臺線程 19
2.3.2 內存 22
2.4 Checkpoint技術 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關鍵特性 45
2.6.1 插入緩沖 46
2.6.2 兩次寫 53
2.6.3 自適應哈希索引 55
2.6.4 異步IO 57
2.6.5 刷新鄰接頁 58
2.7 啟動、關閉與恢復 58
2.8 小結 61
第3章 文件 62
3.1 參數文件 62
3.1.1 什么是參數 63
3.1.2 參數類型 64
3.2 日志文件 65
3.2.1 錯誤日志 66
3.2.2 慢查詢日志 67
3.2.3 查詢日志 72
3.2.4 二進制日志 73
3.3 套接字文件 83
3.4 pid文件 83
3.5 表結構定義文件 84
3.6 InnoDB存儲引擎文件 84
3.6.1 表空間文件 85
3.6.2 重做日志文件 86
3.7 小結 90
第4章 表 91
4.1 索引組織表 91
4.2 InnoDB邏輯存儲結構 93
4.2.1 表空間 93
4.2.2 段 95
4.2.3 區 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 行溢出數據 110
4.3.4 Compressed和Dynamic行記錄格式 117
4.3.5 CHAR的行結構存儲 117
4.4 InnoDB數據頁結構 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數據頁結構示例分析 125
4.5 Named File Formats機制 132
4.6 約束 134
4.6.1 數據完整性 134
4.6.2 約束的創建和查找 135
4.6.3 約束和索引的區別 137
4.6.4 對錯誤數據的約束 137
4.6.5 ENUM和SET約束 139
4.6.6 觸發器與約束 139
4.6.7 外鍵約束 142
4.7 視圖 144
4.7.1 視圖的作用 144
4.7.2 物化視圖 147
4.8 分區表 152
4.8.1 分區概述 152
4.8.2 分區類型 155
4.8.3 子分區 168
4.8.4 分區中的NULL值 172
4.8.5 分區和性能 176
4.8.6 在表和分區間交換數據 180
4.9 小結 182
第5章 索引與算法 183
5.1 InnoDB存儲引擎索引概述 183
5.2 數據結構與算法 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統計 212
5.6 B+樹索引的使用 215
5.6.1 不同應用中B+樹索引的使用 215
5.6.2 聯合索引 215
5.6.3 覆蓋索引 218
5.6.4 優化器選擇不使用索引的情況 219
5.6.5 索引提示 221
5.6.6 Multi-Range Read優化 223
5.6.7 Index Condition Pushdown(ICP)優化 226
5.7 哈希算法 227
5.7.1 哈希表 228
5.7.2 InnoDB存儲引擎中的哈希算法 229
5.7.3 自適應哈希索引 230
5.8 全文檢索 231
5.8.1 概述 231
5.8.2 倒排索引 232
5.8.3 InnoDB全文檢索 233
5.8.4 全文檢索 240
5.9 小結 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 不可重復讀 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 小結 284
第7章 事務 285
7.1 認識事務 285
7.1.1 概述 285
7.1.2 分類 287
7.2 事務的實現 294
7.2.1 redo 294
7.2.2 undo 305
7.2.3 purge 317
7.2.4 group commit 319
7.3 事務控制語句 323
7.4 隱式提交的SQL語句 328
7.5 對于事務操作的統計 329
7.6 事務的隔離級別 330
7.7 分布式事務 335
7.7.1 MySQL數據庫分布式事務 335
7.7.2 內部XA事務 340
7.8 不好的事務習慣 341
7.8.1 在循環中提交 341
7.8.2 使用自動提交 343
7.8.3 使用自動回滾 344
7.9 長事務 347
7.10 小結 349
第8章 備份與恢復 350
8.1 備份與恢復概述 350
8.2 冷備 352
8.3 邏輯備份 353
8.3.1 mysqldump 353
8.3.2 SELECT...INTO OUTFILE 360
8.3.3 邏輯備份的恢復 362
8.3.4 LOAD DATA INFILE 362
8.3.5 mysqlimport 364
8.4 二進制日志備份與恢復 366
8.5 熱備 367
8.5.1 ibbackup 367
8.5.2 XtraBackup 368
8.5.3 XtraBackup實現增量備份 370
8.6 快照備份 372
8.7 復制 376
8.7.1 復制的工作原理 376
8.7.2 快照+復制的備份架構 380
8.8 小結 382
第9章 性能調優 383
9.1 選擇合適的CPU 383
9.2 內存的重要性 384
9.3 硬盤對數據庫性能的影響 387
9.3.1 傳統機械硬盤 387
9.3.2 固態硬盤 387
9.4 合理地設置RAID 389
9.4.1 RAID類型 389
9.4.2 RAID Write Back功能 392
9.4.3 RAID配置工具 394
9.5 操作系統的選擇 397
9.6 不同的文件系統對數據庫性能的影響 398
9.7 選擇合適的基準測試工具 399
9.7.1 sysbench 399
9.7.2 mysql-tpcc 405
9.8 小結 410
第10章 InnoDB存儲引擎源代碼的編譯和調試 411
10.1 獲取InnoDB存儲引擎源代碼 411
10.2 InnoDB源代碼結構 413
10.3 MySQL 5.1版本編譯和調試InnoDB源代碼 415
10.3.1 Windows下的調試 415
10.3.2 Linux下的調試 418
10.4 cmake方式編譯和調試InnoDB存儲引擎 423
10.5 小結 424
熟知概念
第1章 MySQL體系結構和存儲引擎
定義數據庫和實例
數據庫(database):物理操作系統文件或其他形式文件的集合;是依照某種數據模型組織起來并存放于二級存儲器中的數據集合。通常表現為:frm、MYD、MYI、ibd結尾的文件。
實例(instance):MySQL數據庫由后臺線程及一個共享內存區組成。共享內存可以被運行的后臺線程所共享。數據庫實例是真正用于操作數據庫文件的。
通俗理解:
實例:程序,是位于用戶與操作系統之間的一層數據管理軟件,用戶對與數據庫數據的任何操作(增刪改查),都是在數據庫實例下進行的,應用程序只有通過數據庫實例才能和數據庫打交道。
數據庫:由一個個文件組成(一般來說都是二進制文件),要對這些文件進行增刪改查,則需要通過數據庫實例來完成。
關系:MySQL數據庫中,實例和數據庫的關系通常是一一對應的,即一個實例對應一個數據庫。集群情況下,可能存在一個數據庫被多個實例使用的情況。
MySQL數據庫實例在系統上的表現為:一個進程。
MySQL體系結構
由圖可見:mysql由以下組成
Connection Pool:連接池組件,管理緩沖用戶連接,線程處理等需要緩存的需求
Management Serveices &Utilities:管理服務和工具組件
SQL Interface:SQL接口組件,接受用戶的SQL命令,并且返回用戶需要查詢的結果。比如select from就是調用SQL Interface。
Parser:查詢分析器組件SQL命令傳遞到解析器的時候會被解析器驗證和解析。解析器是由Lex和YACC實現的,是一個很長的腳本。
- 主要功能:
a . 將SQL語句分解成數據結構,并將這個結構傳遞到后續步驟,以后SQL語句的傳遞和處理就是基于這個結構的 。
b. 如果在分解構成中遇到錯誤,那么就說明這個sql語句是不合理的
Optimizer:優化器組件,QL語句在查詢之前會使用查詢優化器對查詢進行優化。他使用的是“選取-投影-聯接”策略進行查詢。用一個例子就可以理解: select uid,name fromuser where gender = 1;這個select 查詢先根據where 語句進行選取,而不是先將表全部查詢出來以后再進行gender過濾。這個select查詢先根據uid和name進行屬性投影,而不是將屬性全部取出以后再進行過濾,將這兩個查詢條件聯接起來生成最終查詢結果
Cache和Buffer:緩沖組件,如果查詢緩存有命中的查詢結果,查詢語句就可以直接去查詢緩存中取數據。這個緩存機制是由一系列小緩存組成的。比如表緩存,記錄緩存,key緩存,權限緩存等
Engine:插件式存儲引擎,存儲引擎是MySql中具體的與文件打交道的子系統。也是Mysql最具有特色的一個地方。Mysql的存儲引擎是插件式的。它根據MySql AB公司提供的文件訪問層的一個抽象接口來定制一種文件訪問機制(這種訪問機制就叫存儲引擎)。MySQL插件式的存儲引擎架構提供了一系列標準的管理和服務支持,這些標準和存儲引擎本書無關。注意:存儲引擎是基于表的,而不是基于數據庫
物理文件:不展開。
MYSQL存儲引擎
好處:每個存儲引擎都有各自的特點,能夠根據具體的應用建立不同存儲引擎表。
InnoDB
1.從mysql5.5.8開始,InnoDB是默認的存儲引擎。
2.設計目標:面向在線事物處理(OLTP)
3.特點:行鎖設計,支持外鍵,支持非鎖定讀。即默認讀取操作不會產生鎖。
InnoDB把數據放在一個邏輯的表空間中,這個表空間像黑盒一樣由InnoDB進行管理。可以將每個InnoDB存儲引擎的表單獨存放到一個獨立的ibd文件中。支持用裸設備(row disk)來建立其表空間。通過多版本并發控制來獲得高并發性。實現了SQL標準的4中隔離級別,默認為REPEATABLE級別。對表中數據的存儲,采用了聚集的方式。因此,每張表的存儲都是按照主鍵的順序進行存放。如果沒有顯式的在表定義中指定主鍵,InnoDB存儲引擎會為每一行生成一個6字節的ROWID,并以此作為主鍵。
MyISAM存儲引擎
不支持事務、表鎖設計,支持全文索引,主要面向一些OLAP數據庫應用。5.5.8之前,mysql的默認存儲引擎。另外,它的緩沖池只緩沖索引文件,而不緩沖數據文件。MyISAM存儲引擎表由MYD和MYI組成,MYD用來存放數據文件,MYI用來存放索引文件。
NDB存儲引擎
集群存儲引擎,其結構是share nothing的集群機構,因此能提供更高的可用性。特點是數據全部放在內存中,5.1開始,可以將非索引數據放在磁盤上,因此主鍵查找的速度極快,并通過添加NDB數據存儲節點可以線性的提高數據庫性能,是高可用,高性能的集群系統。
NDB存儲引擎的鏈接操作(JOIN)是在mysql數據庫層完成的,而不是在存儲引擎層完成的。這意味著復雜的鏈接操作需要巨大的網絡開銷,因此查詢速度很慢。
Memory存儲引擎
將表中數據存放在內存中,如果數據庫重啟或發生奔潰,表中數據都將消失。適合存數據臨時表及數據倉庫中的緯度表。默認使用哈希索引,而不是B+樹索引。只支持表鎖,并發性能較差,不支持TEXT和BLOB列類型。存儲變長(varchar)字段時是按照定長(char)字段的方式進行的,浪費內存。
Archive存儲引擎
只支持insert和select操作,5.1以后支持索引。使用zlib算法將數據行進行壓縮后存儲,壓縮比可達1:10。適合存儲歸檔數據,如日志信息。使用行鎖來實現高并發的插入操作,但是其本身不是事務安全的存儲引擎,設計目標主要是提供高速的插入和壓縮功能。
Federated存儲引擎
不存放數據,只是指向一臺遠程mysql數據庫服務器上的表。只支持mysql數據庫表,不支持異構數據庫表。
Maria存儲引擎
新開發的引擎,主要是用來取代原有的MyISAM存儲引擎,從而成為MySQL的默認存儲引擎。支持緩存數據和索引文件,應用了行鎖設計,提供了MVCC功能,支持事務和非事務安全的選項,以及更好的BLOB字符類型的處理性能。
其他存儲引擎:不展開
show engines:查看當前mysql所支持的存儲引擎。
通過查找information_schema架構下的engines表查看當前mysql所支持的存儲引擎。
create table mytest Engine=MyISAM;
create table mytest Engine=InnoDB;
create table mytest Engine=ARCHIVE;
連接MySQL
一個連接進程和MySQL數據庫實例進行通信。本質上是進程通信。
TCP/IP:我們平時所用的連接方式。通過TCP/IP連接到MySQL實例時,MySQL數據庫會先檢查一張權限視圖,用來判斷發起請求的客戶端IP是否允許連接到MySQL實例。該視圖在mysql架構下,表名為user.
命名管道和共享內存:
如果兩個需要進程通信的進程在同一臺服務器上,那么可以適用命名管道,在配置文件中啟用--enable-named-pipe選項。
共享內存的連接方式:通過在配置文件中添加--shared-memory實現。如果想使用共享內存的方式,在連接時,MySQL客戶端還必須使用--protocol=memory選項。
UNIX域套接字:
Linux或UNIX環境下,mysql客戶端和數據庫實例在一臺服務器上的情況下可以使用。用戶可以在配置文件中指定套接字文件的路徑,如--socket=/tmp/mysql.sock.當數據庫實例啟動后,用戶可以通過下列命令來進行UNIX域套接字文件的查找:
mysql > SHOW VARTABLES LIKE 'socket';
感覺這種連接方式不會常用的。
第2章 InnoDB存儲引擎
Innodb體系結構
首先以一張圖簡單展示 InnoDB 的存儲引擎的體系架構.從圖中可見, InnoDB 存儲引擎有多個內存塊,這些內存塊組成了一個大的內存池,主要負責如下工作:
- 維護所有進程/線程需要訪問的多個內部數據結構
- 緩存磁盤上的數據, 方便快速讀取, 同時在對磁盤文件修改之前進行緩存
-
重做日志(redo log)緩沖
image.png
后臺線程的主要作用是負責刷新內存池中的數據,保證緩沖池中的內存緩存的是最新數據;將已修改數據文件刷新到磁盤文件;保證數據庫發生異常時 InnoDB 能恢復到正常運行 的狀態
后臺線程
InnoDB 使用的是多線程模型, 其后臺有多個不同的線程負責處理不同的任務
1. Master Thread
這是最核心的一個線程,主要負責將緩沖池中的數據異步刷新到磁盤,保證數據的一致性,包括贓頁的刷新、合并插入緩沖、UNDO 頁的回收等.
2. IO Thread
在 InnoDB 存儲引擎中大量使用了異步 IO 來處理寫 IO 請求, IO Thread 的工作主要是負責這些 IO 請求的回調.
可以通過命令來觀察 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 來回收已經使用比分配的 undo頁. InnoDB 支持多個 Purge Thread, 這樣做可以加快 undo 頁的回收
InnoDB 引擎默認設置為4個 Purge Thread:
SHOW VARIABLES LIKE "innodb_purge_threads"\G
4. Page Cleaner Thread
Page Cleaner Thread 是新引入的,其作用是將之前版本中臟頁的刷新操作都放入單獨的線程中來完成,這樣減輕了 Master Thread 的工作及對于用戶查詢線程的阻塞
內存
1. 緩沖池
緩沖池簡單來說就是一塊內存區域.在數據庫中進行讀取頁的操作,首先將從磁盤讀到的頁存放在緩沖池中,下一次讀取相同的頁時,首先判斷該頁是不是在緩沖池中,若在,稱該頁在緩沖池中被命中,直接讀取該頁.否則,讀取磁盤上的頁.
對于數據庫中頁的修改操作,首先修改在緩沖池中頁,然后再以一定的頻率刷新到磁盤,并不是每次頁發生改變就刷新回磁盤.
緩沖池的大小直接影響數據庫的整體性能,對于 InnoDB 存儲引擎而言,緩沖池配置通過參數 innodb_buffer_pool_size
來設置. 下面顯示本機虛擬機上一臺 MySQL 數據庫配置:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'\G
緩沖池中緩存的數據頁類型有:索引頁、數據頁、 undo 頁、插入緩沖、自適應哈希索引、 InnoDB 的鎖信息、數據字典信息等.索引頁和數據頁占緩沖池的很大一部分.
下圖顯示 InnoDB 存儲引擎總內存的結構情況.
2. 重做日志緩沖
InnoDB 存儲引擎先將重做日志信息放入這個緩沖區,然后以一定頻率將其刷新到重做日志文件.重做日志文件一般不需要設置得很大,因為在下列三種情況下重做日志緩沖中的內容會刷新到磁盤的重做日志文件中.
- Master Thread 每一秒將重做日志緩沖刷新到重做日志文件
- 每個事物提交時會將重做日志緩沖刷新到重做日志文件
- 當重做日志緩沖剩余空間小于1/2時,重做日志緩沖刷新到重做日志文件
3. 額外的內存池
在 InnoDB 存儲引擎中, 對一些數據結構本身的內存進行分配時,需要從額外的內存池中進行申請.例如,分配了緩沖池,但是每個緩沖池中的幀緩沖還有對應的緩沖控制對象,這些對象記錄以一些諸如 LRU, 鎖,等待等信息,而這個對象的內存需要從額外的內存池中申請.
Checkpoint技術
1,checkpoint產生的背景
數據庫在發生增刪查改操作的時候,都是先在buffer pool中完成的,為了提高事物操作的效率,buffer pool中修改之后的數據,并沒有立即寫入到磁盤,這有可能會導致內存中數據與磁盤中的數據產生不一致的情況。
事物要求之一是持久性(Durability),buffer pool與磁盤數據的不一致性的情況下發生故障,可能會導致數據無法持久化。
為了防止在內存中修改但尚未寫入到磁盤的數據,在發生故障重啟數據之后產生事物未持久化的情況,是通過日志(redo log)先行的方式來保證的。
redo log可以在故障重啟之后實現“重做”,保證了事物的持久化的特性,但是redo log空間不可能無限制擴大,對于內存中已修改但尚未提交到磁盤的數據,也即臟頁,也需要寫入磁盤。
對于內存中的臟頁,什么時候,什么情況下,將多少臟頁寫入磁盤,是由多方面因素決定的。
checkpoint的工作之一,就是對于內存中的臟頁,在一定條件下將臟頁刷新到磁盤。
2,checkpoint的分類
按照checkpoint刷新的方式,MySQL中的checkpoint分為兩種,也即sharp checkpoint和fuzzy checkpoint。
sharp checkpoint:在關閉數據庫的時候,將buffer pool中的臟頁全部刷新到磁盤中。
fuzzy checkpoint:數據庫正常運行時,在不同的時機,將部分臟頁寫入磁盤,進刷新部分臟頁到磁盤,也是為了避免一次刷新全部的臟頁造成的性能問題。
Innodb關鍵特性
插入緩沖
當插入數據需要更新非聚集索引時,如果每次都更新則需要進行多次隨機IO,因此將這些值寫入緩沖對相同頁的進行合并提高IO性能。
插入非聚集索引時,先判斷該索引頁是否在緩沖池中,在則直接插入。否則寫入到Insert Buffer對象。
條件:二級索引,索引不能是unique(因為如果是unique則必須保證唯一性,此時得檢查所有索引頁,還是隨機IO了)
Change Buffer:包括Insert Buffer、Delete Buffer、Purge Buffer,update操作包括將記錄標記為已刪除和真正將記錄刪除兩個過程,對應后兩個Buffer。
Insert Buffer內部是一顆B+樹
Merge Insert Buffer三種情況:
對應的索引頁被讀入緩沖池。
對應的索引頁的可用空間小于1/32,則強制進行合并。
Master Thread中的合并插入緩沖。
兩次寫
在對臟頁刷新到磁盤時,如果某一頁還沒寫完就宕機,此時該頁數據已經混亂無法通過redo實現恢復。innodb提供了doublewrite機制,其刷新臟頁步驟如下:
- 先將臟頁數據復制到doublewrite buffer中(2MB內存)
- 將doublewrite buffer分兩次,每次1MB寫入到doublewrite磁盤(2MB)中。
- 馬上同步臟頁數據到磁盤。對于數據混亂的頁則可以從doublewrite中讀取到,該頁寫到共享表空間。
自適應哈希索引
InnoDB存儲引擎會監控對表上索引的查找,如果觀察到建立哈希索引可以帶來速度的提升,則建立哈希索引,所以稱之為自適應(adaptive) 的。自適應哈希索引通過緩沖池的B+樹構造而來,因此建立的速度很快。而且不需要將整個表都建哈希索引,InnoDB存儲引擎會自動根據訪問的頻率和模式 來為某些頁建立哈希索引。
異步IO
linux和windows中提供異步IO,其可以對連續的頁做合并連續頁的IO操作使隨機IO變順序IO。
刷新鄰近頁
刷新頁時判斷相鄰頁是否也是臟頁。
一, InnoDB是什么
1、創始人為Heikki Tuuri(1964,芬蘭赫爾辛基),由Innobase Oy公司開發,mysql5.5版本開始是默認的表存儲引擎。
2.特點是行鎖設計、支持MVCC、支持外鍵、提供一致性非鎖定讀,最有效的使用內存和CPU.
3.已應用于各大型網站,如:Google、Yahoo!、Facebook等。
二、InnoDB版本
1.早期版本隨mysql數據庫版本更新而更新
2.mysql5.1以后,支持存儲引擎開發商以動態的形式加載。所以支持兩個版本:
一個是靜態編譯的innoDB版本(老版本)【支持ACID、行鎖設計、MVCC】
另一個是動態加載的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的體系結構
由圖可見,InnoDB有多個內存塊,可以認為這些內存塊組成了一個大的內存池,負責如下工作:維護所有進程/線程需要訪問的多個內部數據結構;緩存磁盤上的數據,方便快速的讀取,同事在對磁盤文件的數據修改之前在這里緩存;重做日志(redo log)緩沖等
后臺線程的主要作用是負責刷新內存池中的數據,保證緩沖池中的內存緩存的是最近的數據。此外將已修改的數據文件刷新到磁盤文件,同事保證在數據庫發生異常的情況下InnoDB能恢復到正常運行狀態。
第3章 文件
都有什么文件
當MySQL實例啟動時,MySQL會先去讀一個配置參數文件,用來尋找數據庫的各種文件所在位置以及指定某些初始化參數,這些參數通常定義了某種內存結構有多大等設置。默認情況下,MySQL實例會按照一定的次序去取,你只需通過命令mysql --help|grep my.cnf來尋找即可。
MySQL參數文件的作用和Oracle的參數文件極其類似;不同的是,Oracle實例啟動時若找不到參數文件,是不能進行裝載(mount)操作的。MySQL稍微有所不同,MySQL實例可以不需要參數文件。
和Oracle參數文件不同的是,Oracle的參數文件分為二進制的參數文件(spfile)和文本類型的參數文件(init.ora),而MySQL的參數文件僅是文本的,方便的是,你可以通過一些常用的編輯軟件(如vi和emacs)進行參數的編輯。
可以把數據庫參數看成一個鍵/值對。可以通過show variables查看所有的參數,或通過like來過濾參數名。
從MySQL 5.1版本開始,可以通過information_schema架構下的GLOBAL_VARIABLES視圖來進行查找,如下所示:
select * from GLOBAL_VARIABLES where VARIABLE_NAME like 'innodb_buffer%'\G;
show variables like 'innodb_buffer%'\G
參數類型
MySQL參數文件中的參數可以分為兩類:動態(dynamic)參數和靜態(static)參數。動態參數意味著你可以在MySQL實例運行中進行更改;靜態參數說明在整個實例生命周期內都不得進行更改,就好像是只讀(read only)的。
可以通過SET命令對動態的參數值進行修改,SET的語法如下:
SET
| [global|session] system_var_name=expr
| [@@global.|@@session.|@@] system_var_name=expr
這里可以看到global和session關鍵字,它們表明該參數的修改是基于當前會話還是整個實例的生命周期。
有些動態參數只能在會話中進行修改,如autocommit;
有些參數修改完后,在整個實例生命周期中都會生效,如binlog_cache_size;
而有些參數既可以在會話又可以在整個實例的生命周期內生效,如read_buffer_size。
例如: set read_buffer_size=524288;
日志文件:
日志文件類型
MySQL有幾個不同的日志文件,可以幫助你找出mysqld內部發生的事情:
日志文件: 記入文件中的信息類型
錯誤日志:記錄啟動、運行或停止mysqld時出現的問題。
查詢日志:記錄建立的客戶端連接和執行的語句。
更新日志:記錄更改數據的語句。不贊成使用該日志。
二進制日志:記錄所有更改數據的語句。還用于復制。
慢查詢日志:記錄所有執行時間超過long_query_time秒的所有查詢或不使用索引的查詢。
mysql 日志包括:錯誤日志,二進制日志,通用查詢日志,慢日志等
一:通用查詢日志:
記錄建立的客戶端連接和執行的語句
1)show variables like '%verision%';
顯示數據庫版本號,存儲引擎等信息
2)查看當前的通用日志是否開啟
show variables like '%general%';
開啟通用日志查詢: set global general_log = on;
關閉通用日志查詢:set global general_log = off;
3)查看當前慢文件的格式
show variables like '%log_output%';
設置通用日志輸出為表和文件方式:
set global log_output = 'file,table';
二:慢查詢日志:
記錄所有執行時間超過long_query_time秒的所有查詢或者不適用索引的查詢
默認情況下,MySQL不開啟慢查詢日志,long_query_time的默認值為10,即運行時間超過10s的語句是慢查詢語句。
一般來說,慢查詢發生在大表中,且查詢的字段沒有建立索引,此時,要匹配查詢的字段會對全表進行掃描,耗時查long_query_time表
查看當前慢查詢日志的開啟情況:
show variables like '%query%';
查詢當前慢查詢的語句個數:
show global status like '%slow%';
可以通過查詢語句查看慢查詢的語句:
select * from mysql.slow_log;
三:錯誤日志
MySQL錯誤日志是記錄MySQL運行過程中較為嚴重的警告和錯誤信息,以及MySQL每次啟動和關閉的詳細信息。錯誤日志的命名通常為 服務器主機名.err
查看錯誤日志的詳細信息:
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
四:二進制日志
包含了所有更新了的數據或者潛在更新了的數據,
包含關于每個更新數據庫的語句的執行時間信息
目的:
盡可能將數據庫恢復到事故故障點,因為二進制日志包含備份后進行的所有更新,用于在主復制服務器上記錄所有將發生送給從服務器的語句
刪除所有二進制文件:
reset master
刪除部分二進制文件:
purge master logs
查看是否啟用二進制日志:
show variables like '%log_bin%';
查看所有的二進制參數
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和應用同在一臺PC上。如果不在同一臺pc上,就沒有辦法連接了。
直接就用tcp方式mysql -h localhost -u root -proot直接登錄了,沒有用套接字這方式。由此也可看出不是必須。
Unix系統下本地連接mysql可以采用Unix域套接字方式,這種方式需要一個套接字文件。套接字文件由參數socket控制。一般在/tmp目錄下,名為mysql.sock /tmp/mysq.sock
SHOW VARIABLES LOKE 'socket'\G
4.pid文件:實例的進程文件
mysql啟動時,會將自己的進程ID寫入一個文件中,該文件為pid文件。由參數pid_file控制,默認位于數據庫目錄下,文件名為主機名.pid
實驗結果如下:
5.mysql表結構文件:
每個表或視圖,都有一個以frm為后綴名的文本文件,記錄該表的表結構定義。
6.InnoDB存儲引擎文件
MySql中每個表存儲引擎都有自己獨有的文件,InnoDB存儲引擎相關的文件主要包括:重做日志文件,表空間文件。
1.表空間文件
InnoDB采用將存儲的數據按表空間(tablespace)進行存放的設計。在默認配置下會有一個初始大小為10MB,名為ibdata1的文件。該文件就是默認的表空間文件(tablespace file),用戶可以通過參數innodb_data_file_path對其進行設置.
mysql> show variables like 'innodb_data_file_path';
2.重做日志文件
在默認情況下,在InnoDB存儲引擎的數據目錄下會有兩個名為ib_logfile0和ib_logfile1的文件。在MySql官方手冊中將其稱為InnoDB存儲引擎的日志文件,不過更準確的定義應該是重做日志文件(redo log file)。
重做日志文件對于InnoDB存儲引擎很重要,它們記錄了對于InnoDB存儲引擎的事務日志。
當實例或介質 failure時,重做日志文件就派上用場了。例如,數據庫由于所在主機掉電導致實例失敗,InnoDB存儲引擎會使用重做日志恢復到掉電前的時刻,以此來保證數據的完整性。
每個InnoDB存儲引擎至少有1個重做日志文件組,每個文件組下至少有2個重做日志文件,如默認的ib_logfile0和ib_logfile1。
第4章 表
4.1 索引組織表
在InnoDB存儲引擎中,因為表都是按照主鍵的順序進行存放的,這種存放方式,我們成為索引組織表(IOT)
那么,在整個創建表的過程中,InnoDB是怎么去創建主鍵的
1.顯式的創建主鍵Praimary key
2.判斷表中是否有非空唯一索引,如果有,則為主鍵
3.如果都不符合上述1/2的條件,則會生成UUID的一個隱式主鍵(6字節大)
在創建的的過程中,如果表中,有多個非空唯一索引的時候,則按照創建索引的順序,以第一個非空唯一索引為準來創建。
SELECT a,b,c,d,_rowid FROM test;
如果表中有多個非空唯一索引時,InnoDB將選擇建表時第一個定義的非空唯一索引為主鍵,通過_rowid可以顯示表的主鍵,但是只能查看單個列作為主鍵的情況,對于多列組成的主鍵則不可以。
4.2InnoDB邏輯存儲結構
從InnoDB存儲引擎的邏輯存儲結構看,所有數據都被邏輯地存放在一個空間中,稱之為表空間(tablespace)。表空間又由段(segment)、區(extent)、頁(page)組成。頁在一些文檔中有時也稱為(block),InnoDB存儲引擎的邏輯存儲結構大致如圖:
表空間
表空間可以看做是InnoDB存儲引擎邏輯結構的最高層,所有的數據都存放在表空間中。默認情況下InnoDB存儲引擎有一個共享表空間ibdata1,即所有數據都存放在這個表空間內。如果用戶啟動了innodb_file_per_table,則每個表內的數據可以單獨放到一個表空間內,但要注意的是每張表的表空間內存放的只是數據、索引和插入緩存Bitmap頁,而其他類的數據,如回滾(undo)信息,插入緩存索引頁、系統事務信息、二次寫緩存(Double write buffer)等還是存放在原來的共享表空間內。
即使設置了innodb_file_per_table為ON了,共享表空間還是會不斷地增加其大小。
段
表空間由各個段組成,比如數據段,索引段,回滾段等。
區
區由連續的頁組成,在任何情況下區的大小都是1M。InnoDB存儲引擎一次從磁盤申請大概4-5個區。在默認情況下,頁的大小為16KB,即一個區中有大概64個連續的頁。
頁
InnoDB磁盤管理的最小單位。
- B樹節點= 一個物理Page(16K)
- 數據按16KB切片為Page 并編號
- 編號可映射到物理文件偏移(16K * N)
- B+樹葉子節點前后形成雙向鏈表
image - 增刪改查之后
- 有效Node組成雙向鏈表
- 中間存在空洞
- 全表掃描時IO可能不連續
關于頁的詳細結構,參考:MySQL的InnoDB索引原理詳解
行
數據是按行進行存放的。
行記錄格式
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行設計目標是能高效存放數據。簡單來說,如果一個頁中存放的行數據越多,其性能就越高。
變長字段長度列表 | NULL標志位 | 記錄頭信息 | 列1數據 | 列2數據 | …… |
---|
Redundant行記錄格式
Redundant是Mysql5.0之前的,不過多介紹。
行溢出數據
行溢出數據
InnoDB存儲引擎可以將一條記錄中的某些數據存儲在真正的數據頁面之外,即作為行溢出數據。一般認為BLOB、LOB這類的大對象列類型的存儲會把數據存放在數據頁面之外。但是,這個理解有點偏差,BLOB可以不將數據放在溢出頁面,而即使是varchar列數據類型,依然有可能存放為行溢出數據。
MySQL數據庫的varchar字段,它可以存放65536字節的數據,比oracle和sqlserver大多了,但是在使用varchar時也有幾點要注意;
- 1、65536只是這么說,要建表的時候指定一個字段65536仍然會報錯,因為字段本身還有其他開銷,實際只能存放65532字節。
- 2、65532字節并不是每個varchar字段都可以設置的,他是一個總和,也就是說如果有2個varchar字段的表,那么每個varchar字段只能設置65532/2的值。
- 3、建表時要注意編碼格式哦,varchar(65532)代表的是字節數,如果使用GBK或者UTF-8那就無法建立成功了,因為GBK一個字符占用2字節,UTF-8一個字符占用3字節
頁(PAGE)是innoDB存儲引擎的最小存儲單位,默認大小為16KB,及16384字節,行數據存儲在頁中;那么一行數據如果超過了一個頁能夠存儲的大小怎么辦?比如上面說的varchar(65532),65532字節該如何存儲?這個時候就會發生行溢出。
行溢出:
InnoDB存儲引擎可以將一條記錄中的某些數據存儲在真正的數據頁面之外,一般為BLOB\LOB這類的大對象列類型。但是也不是絕對,BLOB可以不將數據放在溢出頁面,而且即便是VARCHAR列數據類型,依然有可能被存放為行溢出數據
Compressed和Dynamic行記錄格式
InnoDB Plugin引入了新的文件格式(file format,可以理解為新的頁格式),對于以前支持的Compact和Redundant格式將其稱為Antelope文件格式,新的文件格式稱為Barracuda。Barracuda文件格式下擁有兩種新的行記錄格式Compressed和Dynamic兩種。新的兩種格式對于存放BLOB的數據采用了完全的行溢出的方式,在數據頁中只存放20個字節的指針,實際的數據都存放在BLOB Page中,而之前的Compact和Redundant兩種格式會存放768個前綴字節。
下圖是Barracuda文件格式的溢出行:
Compressed行記錄格式的另一個功能就是,存儲在其中的行數據會以zlib的算法進行壓縮,因此對于BLOB、TEXT、VARCHAR這類大長度類型的數據能進行非常有效的存儲。
char的行結構存儲
在InnoDB引擎內部對于char類型在多字節字符集類型(如utf8)的存儲,char很明確的被視為了變長類型,對于未能占滿長度的字符還是填充長度。可以說,在多字節字符集的情況下,char和varchar的行存儲基本是沒有區別的。
4.4InnoDB數據頁結構
InnoDB數據頁由以下七個部分組成:
File Header | Page Header | Infimum+Supremum Records | User Records | Free Space | Page Directory | File Trailer |
---|
頁是InnoDB存儲引擎管理數據庫的最小磁盤單位。頁類型為B-tree node的頁,存放的即是表中行的實際數據了。
InnoDB數據頁由以下七個部分組成,如圖所示:
- File Header(文件頭)。
- Page Header(頁頭)。
- Infimun+Supremum Records。
- User Records(用戶記錄,即行記錄)。
- Free Space(空閑空間)。
- Page Directory(頁目錄)。
- File Trailer(文件結尾信息)。
!](http://upload-images.jianshu.io/upload_images/2830277-3092659511a48a88.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
File Header、Page Header、File Trailer的大小是固定的,用來標示該頁的一些信息,如Checksum、數據所在索引層等。其余部分為實際的行記錄存儲空間,因此大小是動態的。
File Header
File Header用來記錄頁的一些頭信息。
Page Header
接著File Header部分的是Page Header,用來記錄數據頁的狀態信息
Infimum和Supremum記錄
在InnoDB存儲引擎中,每個數據頁中有兩個虛擬的行記錄,用來限定記錄的邊界。Supremum和Infimum分別是主鍵值得上界和下界,這兩個值在頁創建時被建立,并且在任何情況下不會被刪除。
User Records與FreeSpace
User Records即實際存儲行記錄的內容。再次強調,InnoDB存儲引擎表總是B+樹索引組織的。
Free Space指的就是空閑空間,同樣也是個鏈表數據結構。當一條記錄被刪除后,該空間會被加入空閑鏈表中。
Page Directory
Page Directory(頁目錄)中存放了記錄的相對位置。需要記住,B+樹索引本身并不能找到具體的一條記錄,B+樹索引能找到的只是該記錄所在的頁。數據庫把頁載入內存,然后通過Page Directory再進行二分查找。
File Trailer
用來保證頁能夠完整的寫入磁盤,來作比較以此來保證頁的完整性。
Named File Formats機制
InnoDB存儲引擎通過Named File Formats機制來解決不同版本下頁結構兼容性的問題。
4.6約束
數據完整性
完整性是指數據的準確性和一致性,而完整性檢查就是指檢查數據的準確性和一致性。mysql數據庫管理系統提供了一直機制來檢查數據庫中的數據是否滿足規定的條件。以保證數據庫中數據的準確性和一致性,這種機制就是約束。
- mysql所支持的完整性約束
NOT NULL(NK)
DEFAULT
UNIQUE KEY(UK)約束字段的值是唯一的
PRIMATYKEY(PK)
AUTO_INCREMENT
FOREIGN KEY(FK)
束和索引的區別
mysql 約束和索引
相同點: 保證證數據的完整性
區別: 索引是從數據結構的角度來保證數據完整性, 而 約束是一個邏輯概念,用來保證數據完整性.
保證數據完整性的方法:
一. 對錯誤數據的約束
設置sql_mode 為 嚴格模式, 來提示報錯而不是警告
服務器配置: my.cnf sql_mode ='STRICT_TRANS_TABLES' ;
客戶端 使用 : set sql_mode = 'STRICT_TRANS_TABLES' ;
比如在非嚴格模式下, 對字段已設置為not null , 插入了 非法日期的值: 比如 2009-02-30.
二. ENUM 和 SET 約束(針對數據類型).
比如假設 性別 只允許兩種, male, female. enum('male', 'female'), 也可以 enum(0, 1). 如果設置為 tinyint(1), 值就可以存在 0-9.
三. 觸發器約束
通過創建觸發器來過濾錯誤的數據.一般用在字讀間的計算.
四.外鍵
InnoDB支持外鍵, 可以通過外鍵來保證數據的完整性. 比如 一個用戶表, 對應有張子表來存儲一些額外的信息,
子表通過外鍵就可以達到只要主表有 delete 和 update 的操作,對應的數據也會 delete 和 update.
MyISAM 表不支持外鍵,可以只用 觸發器來控制數據完整性.
4.7 視圖
視圖的主要用途之一是被用作一個抽象裝置,特別是對于一些應用程序,程序本身不需要關心基表的結構,只需要按照視圖定義來獲取數據或者更新數據。因此,視圖同時在一定程度上起到一個安全層的作用。
Mysql本身不支持物化視圖,需要采用SELECT然后INSERT類似這種的方式導入視圖的數據到表中。
4.8 分區
0、mysql數據庫分區的由來?
1)傳統不分區數據庫痛點
mysql數據庫中的數據是以文件的形勢存在磁盤上的,默認放在/mysql/data下面(可以通過my.cnf中的datadir來查看),
一張表主要對應著三個文件,一個是frm存放表結構的,一個是myd存放表數據的,一個是myi存表索引的。
2)數據庫分區處理
如果一張表的數據量太大的話,那么myd,myi就會變的很大,查找數據就會變的很慢,這個時候我們可以利用mysql的分區功能,在物理上將這一張表對應的三個文件,分割成許多個小塊,這樣呢,我們查找一條數據時,就不用全部查找了,只要知道這條數據在哪一塊,然后在那一塊找就行了。如果表的數據太大,可能一個磁盤放不下,這個時候,我們可以把數據分配到不同的磁盤里面去。
表分區是Mysql被Oracle收購后推出的一個新特性。
一、表分區通俗解釋
通俗地講表分區是將一大表,根據條件分割成若干個小表。mysql5.1開始支持數據表分區了。
如:某用戶表的記錄超過了600萬條,那么就可以根據入庫日期將表分區,也可以根據所在地將表分區。當然也可根據其他的條件分區。
二、為什么要對表進行分區?
為了改善大型表以及具有各種訪問模式的表的可伸縮性,可管理性和提高數據庫效率。
2.1 表分區要解決的問題:
當表非常大,或者表中有大量的歷史記錄,而“熱數據”卻位于表的末尾。如日志系統、新聞。。此時就可以考慮分區表。【注:此處也可以使用分表,但是會增加業務的復雜性。】
2.2 表分區有如下優點:
1)與單個磁盤或文件系統分區相比,可以存儲更多的數據。
2)對于那些已經失去保存意義的數據,通常可以通過刪除與那些數據有關的分區,很容易地刪除那些數據。
相反地,在某些情況下,添加新數據的過程又可以通過為那些新數據專門增加一個新的分區,來很方便地實現。
同樣的,你可以很快的通過刪除分區來移除舊數據。你還可以優化、檢查、修復個別分區。
3)一些查詢可以得到極大的優化。 可以把一些歸類的數據放在一個分區中,可以減少服務器檢查數據的數量加快查詢。
這主要是借助于滿足一個給定WHERE語句的數據可以只保存在一個或多個分區內,這樣在查找時就不用查找其他剩余的分區。
PS:因為分區可以在創建了分區表后進行修改,所以在第一次配置分區方案時還不曾這么做時,可以重新組織數據,來提高那些常用查詢的效率。
4)涉及到例如SUM()和COUNT()這樣聚合函數的查詢,可以很容易地進行并行處理。
這種查詢的一個簡單例子如
“SELECT salesperson_id, COUNT (orders) as order_total FROM sales GROUP BY salesperson_id;”。
通過“并行”,這意味著該查詢可以在每個分區上同時進行,最終結果只需通過總計所有分區得到的結果。
5)通過跨多個磁盤來分散數據查詢,來獲得更大的查詢吞吐量。
三、mysql分區類型
根據所使用的不同分區規則可以分成幾大分區類型。
子分區
分區其實是對每個分區表的每個分區進行再次分隔,目前只有RANGE和LIST分區的表可以再進行子分區,子分區只能是HASH或者KEY分區。子分區可以將原本的數據進行再次的分區劃分。
四、常見分區操作
五、獲取分區表信息的方法
5.1 show create table 表名
可以查看創建分區表的create語句
舉例:
mysql> show create table foo_list;
5. 2 show table status
可以查看表是不是分區表
舉例:
SHOW TABLE STATUS LIKE ‘foo_range’;
5.3 查看information_schema.partitions表
如下命令可以查看表具有哪幾個分區、分區的方法、分區中數據的記錄數等信息
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';
六、分區適用場景
7.1常見使用場景
1)當數據量很大(過T)時,肯定不能把數據再如到內存中,這樣查詢一個或一定范圍的item是很耗時。另外一般這情況下,歷史數據或不常訪問的數據占很大部分,最新或熱點數據占的比例不是很大。這時可以根據有些條件進行表分區。
2)分區表的更易管理,比如刪除過去某一時間的歷史數據,直接執行truncate,或者狠點drop整個分區,這比detele刪除效率更高
3)當數據量很大,或者將來很大的,但單塊磁盤的容量不夠,或者想提升IO效率的時候,可以把沒分區中的子分區掛載到不同的磁盤上。
4)使用分區表可避免某些特殊的瓶頸,例如Innodb的單個索引的互斥訪問..
5)單個分區表的備份很恢復會更有效率,在某些場景下
總結:可伸縮性,可管理性,提高數據庫查詢效率。
7.2 業務場景舉例
項目中需要動態新建、刪除分區。如新聞表,按照時間維度中的月份對其分區,為了防止新聞表過大,只保留最近6個月的分區,同時預建后面3個月的分區,這個刪除、預建分區的過程就是分區表的動態管理。
第5章 索引與算法
第6章 鎖
鎖 lock latch
鎖是計算機協調多個進程或純線程并發訪問某一資源的機制。
鎖定機制簡單來說,就是數據庫為了保證數據的一致性,而使各種共享資源在被并發訪問變得有序所設計的一種規則。在數據庫中,除傳統的計算資源(CPU、RAM、I/O)的爭用以外,數據也是一種供許多用戶共享的資源。如何保證數據并發訪問的一致性、有效性是所在有數據庫必須解決的一個問題,鎖沖突也是影響數據庫并發訪問性能的一個重要因素。
相對其他數據庫而言,MySQL的鎖機制比較簡單,其最顯著的特點是不同的存儲引擎支持不同的鎖機制。
MySQL大致可歸納為以下3種鎖:
- 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,并發度最低。
- 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖沖突的概率最低,并發度也最高。
- 頁面鎖:開銷和加鎖時間界于表鎖和行鎖之間;會出現死鎖;鎖定粒度界于表鎖和行鎖之間,并發度一般
lock與latch區別
這里要區分鎖中容易令人混淆的概念lock與latch。在數據庫中,lock與latch都可以成為鎖,但兩者有截然不同的含義
- latch 一般稱為閂鎖(輕量級的鎖) 因為其要求鎖定的時間非常短,若遲勛時間長,則應用性能非常差,在InnoDB存儲引擎中,latch有可以分為mutex(互斥鎖)和rwlock(讀寫鎖)其目的用來保證并發線程操作臨界資源的正確性,并且沒有死鎖檢測的機制
lock的對象是事務,用來鎖定的是數據庫中的UI想,如表、頁、行。并且一般lock對象僅在事務commit或rollback后進行釋放(不同事務隔離級別釋放的時間可能不同),此外lock正如大多數數據庫中一樣,是有死鎖機制的。表顯示了lock與latch的不同
image
InnoDB引擎中的鎖
InnoDB與MyISAM的最大不同有兩點:一是支持事務(TRANSACTION);二是采用了行級鎖。行級鎖與表級鎖本來就有許多不同之處,另外,事務的引入也帶來了一些新問題。下面我們先介紹一點背景知識,然后詳細討論InnoDB的鎖問題。
背景知識
事務(Transaction)及其ACID屬性
事務是由一組SQL語句組成的邏輯處理單元,事務具有以下4個屬性,通常簡稱為事務的ACID屬性。
- 原子性(Atomicity):事務是一個原子操作單元,其對數據的修改,要么全都執行,要么全都不執行。
- 一致性(Consistent):在事務開始和完成時,數據都必須保持一致狀態。這意味著所有相關的數據規則都必須應用于事務的修改,以保持數據的完整性;事務結束時,所有的內部數據結構(如B樹索引或雙向鏈表)也都必須是正確的。
- 隔離性(Isolation):數據庫系統提供一定的隔離機制,保證事務在不受外部并發操作影響的“獨立”環境執行。這意味著事務處理過程中的中間狀態對外部是不可見的,反之亦然。
- 持久性(Durable):事務完成之后,它對于數據的修改是永久性的,即使出現系統故障也能夠保持。
銀行轉帳就是事務的一個典型例子。
并發事務處理帶來的問題
相對于串行處理來說,并發事務處理能大大增加數據庫資源的利用率,提高數據庫系統的事務吞吐量,從而可以支持更多的用戶。但并發事務處理也會帶來一些問題,主要包括以下幾種情況。
- 更新丟失(Lost Update):當兩個或多個事務選擇同一行,然后基于最初選定的值更新該行時,由于每個事務都不知道其他事務的存在,就會發生丟失更新問題--最后的更新覆蓋了由其他事務所做的更新。例如,兩個編輯人員制作了同一文檔的電子副本。每個編輯人員獨立地更改其副本,然后保存更改后的副本,這樣就覆蓋了原始文 檔。最后保存其更改副本的編輯人員覆蓋另一個編輯人員所做的更改。如果在一個編輯人員完成并提交事務之前,另一個編輯人員不能訪問同一文件,則可避免此問 題。
- 臟讀(Dirty Reads):一個事務正在對一條記錄做修改,在這個事務完成并提交前,這條記錄的數據就處于不一致狀態;這時,另一個事務也來讀取同一條記錄,如果不加 控制,第二個事務讀取了這些“臟”數據,并據此做進一步的處理,就會產生未提交的數據依賴關系。這種現象被形象地叫做"臟讀"。
- 不可重復讀(Non-Repeatable Reads):一個事務在讀取某些數據后的某個時間,再次讀取以前讀過的數據,卻發現其讀出的數據已經發生了改變、或某些記錄已經被刪除了!這種現象就叫做“不可重復讀”。
- 幻讀(Phantom Reads):一個事務按相同的查詢條件重新讀取以前檢索過的數據,卻發現其他事務插入了滿足其查詢條件的新數據,這種現象就稱為“幻讀”。
事務隔離級別
- 在上面講到的并發事務處理帶來的問題中,“更新丟失”通常是應該完全避免的。但防止更新丟失,并不能單靠數據庫事務控制器來解決,需要應用程序對要更新的數據加必要的鎖來解決,因此,防止更新丟失應該是應用的責任。
- “臟讀”、“不可重復讀”和“幻讀”,其實都是數據庫讀一致性問題,必須由數據庫提供一定的事務隔離機制來解決。
數據庫實現事務隔離的方式,基本上可分為以下兩種。
- 一種是在讀取數據前,對其加鎖,阻止其他事務對數據進行修改。
- 另一種是不用加任何鎖,通過一定機制生成一個數據請求時間點的一致性數據快照(Snapshot),并用這個快照來提供一定級別(語句級或事務級)的一 致 性讀取。從用戶的角度來看,好像是數據庫可以提供同一數據的多個版本,因此,這種技術叫做數據多版本并發控制(MultiVersion Concurrency Control,簡稱MVCC或MCC),也經常稱為多版本數據庫。
數據庫的事務隔離越嚴格,并發副作用越 小,但付出的代價也就越大,因為事務隔離實質上就是使事務在一定程度上 “串行化”進行,這顯然與“并發”是矛盾的。同時,不同的應用對讀一致性和事務隔離程度的要求也是不同的,比如許多應用對“不可重復讀”和“幻讀”并不敏 感,可能更關心數據并發訪問的能力。
為了解決“隔離”與“并發”的矛盾,ISO/ANSI SQL92定義了4個事務隔離級別,每個級別的隔離程度不同,允許出現的副作用也不同,應用可以根據自己的業務邏輯要求,通過選擇不同的隔離級別來平衡 “隔離”與“并發”的矛盾。表20-5很好地概括了這4個隔離級別的特性。
最后要說明的是:各具體數據庫并不一定完全實現了上述4個隔離級別,例如,Oracle只提供Read committed和Serializable兩個標準隔離級別,另外還提供自己定義的Read only隔離級別;SQL Server除支持上述ISO/ANSI SQL92定義的4個隔離級別外,還支持一個叫做“快照”的隔離級別,但嚴格來說它是一個用MVCC實現的Serializable隔離級別。MySQL 支持全部4個隔離級別,但在具體實現時,有一些特點,比如在一些隔離級別下是采用MVCC一致性讀,但某些情況下又不是
一致性非鎖定讀(consistent nonlocking read)
一致性非鎖定讀是InnoDB存儲引擎通過多版本控制(multi versioning)的方式來讀取當前執行時間數據庫中的數據。如果被讀的數據行被加了排他鎖,在讀取這行數據的時候并不會等待鎖釋放,而是讀取該行的一個快照數據。 之所以稱為非鎖定讀,因為不需要等待被訪問行的X鎖的釋放。快照數據是指修改行之前的數據版本,該實現通過undo段來完成。非鎖定讀的方式極大提高了數據庫的并發性。在InnoDB存儲引擎中,這是默認的讀取方式。
一致性鎖定讀
在默認情況下,InnoDB存儲引擎對數據采用的是一致性非鎖定讀。但是有些情況下為了保證數據邏輯的一致性,需要對SELECT的操作加鎖。InnoDB存儲引擎對于SELECT語句支持兩種一致性的鎖定讀(locking read)操作。
1、 SELECT …… FOR UPDATE
2、 SELECT …… LOCK IN SHARE MODE
其中,SELECT …… FOR UPDATE對讀取的記錄加一個X鎖(排它鎖),其他事務不能對已鎖定的行加任何鎖。而SELECT …… LOCK IN SHARE MODE是對讀取的記錄加一個S鎖(共享鎖),其他事物可以向被鎖定的行加S鎖,但是如果加X鎖,則會被阻塞。
自增長與鎖
自增長在數據庫中是一種非常常見的一種屬性,也是很多DBA或開發人員或者DBA人員首選的主鍵方式。在InnoDB存儲引擎的內存結構中,對每個含有自增長值的表都有一個自增長計數器(auto_increment counter)。當對含有自增長的計數器的表進行插入操作時,這個計數器會被初始化,執行如下的語句來得到計數器的值:
select max(auto_inc_col) from test for update;
插入操作會根據這個自增長的計數器值加1賦予自增長列。這個實現方式稱作為AUTO-INC Locking。這種鎖采用一種特殊的表鎖機制,為了提高插入的性能,鎖不是在一個事務完成后才釋放,而是在完成對自增長值插入的SQL語句后立即釋放。
外鍵與鎖
簡單說一下外鍵,外鍵主要用于引用完整性的約束檢查。在InnoDB存儲引擎中,對于一個外鍵列,如果沒有顯示地對這個列加索引,InnoDB存儲引擎會自動對其加一個索引,因為這樣可以避免表鎖。這比Oracle數據庫做得好,Oracle數據庫不會自動添加索引,用戶必須自己手動添加,這也導致了Oracle數據庫中可能產生死鎖。
鎖的算法
阻塞
數據庫阻塞的現象:第一個連接占有資源沒有釋放,而第二個連接需要獲取這個資源。如果第一個連接沒有提交或者回滾,第二個連接會一直等待下去,直到第一個連接釋放該資源為止。對于阻塞,數據庫無法處理,所以對數據庫操作要及時地提交或
者回滾。
死鎖
是指兩個或兩個以上的事務在執行過程中,因爭奪資源而造成的一種互相等待的現象。若無外力作用,事務都將無法推進下去,解決死鎖的最簡單問題是不要有等待,任何的等待都轉換為回滾,并且事務重新開始,但在線上環境,這可能會導致并發性能下降,甚至任何一個事務都不能進行,而這所帶來的問題遠比死鎖的問題更嚴重
解決死鎖的問題最簡單的一種方法是超時,當兩個事務互相等待時,當一個等待時間超過設置的某一閾值時,其中一個事務回滾,另一個等待的事務就能繼續運行了,在InnoDB存儲引擎中,參數innodb_lock_wait_timeout用來設置超時時間
超時機制雖然簡單,但是其僅通過超時后對事務進行回滾的方式處理,或者說其是根據FIFO的順序選擇回滾對象,但若超時的事務所占權重比較大,如事務操作更新了很多航,占用了較多的undo log,這是采用FIFO方式,就顯得不合適了,因為回滾這個事務的時間相對另一個事務所占用的時間可能會更多
除了超時機制,當前的數據庫還采用wait-for graph(等待圖)的方式來進行死鎖檢測,較之超時的解決方案,這是一種更為主動的死鎖檢測方式。InnoDB存儲引擎也是采用這種方式。wait-for graph要求數據庫保存以下兩種信息
- 鎖的信息鏈表
- 事務等待鏈表
鎖升級
第7章 事務
第8章 備份與恢復
第9章 性能調優
第10章 InnoDB存儲引擎源代碼的編譯和調試
目錄
InnoDB存儲引擎是開源的,這意味著你可以獲得其源代碼,并查看內部的具體實現。任何時候,WHY都比WHAT重要。通過研究源代碼,可以更好地理解數據庫是如何工作的,從而知道如何使數據庫更好地為你工作。如果你有一定的編程能力,則完全可以對InnoDB存儲引擎進行擴展,開發出新的功能模塊來更好地支持你的數據庫應用。
獲取InnoDB存儲引擎源代碼
InnoDB存儲引擎的源代碼被包含在MySQL數據庫的源代碼中,在MySQL的官方網站鏈接為:http://www.mysql.com/downloads/mysql/。下載MySQL數據庫的源代碼即可。 這里有不同操作系統下的源代碼可供下載,一般只需下載Generic Linux的版本即可。通過MySQL官網首頁的Download鏈接,可以迅速地找到GA版本的下載。但是,如果想要下載目前正在開發的MySQL版本,可能在官網找了很久都找不到鏈接。這時,只要把下載的鏈接從www換到dev即可:如http://dev.mysql.com/downloads/mysql,在這里可以找到開發中的MySQL版本的源代碼了。單擊“Download”下載標簽后可以進入下載頁面。當然,如果你有mysql.com賬戶,可以進行登錄。MySQL官方提供了大量的鏡像用來分流下載,你可以根據所在的位置選擇下載速度最快的地址,中國用戶一般可以在“Asia”這里的鏡像下載。
下載的文件是tar.gz結尾的文件,可以通過Linux的tar命令、Windows的WinRAR工具來進行解壓,解壓后得到一個文件夾,這里面就包含了MySQL數據庫的所有源代碼。所有存儲引擎的源代碼都被放在storage的文件夾下,其源代碼結構如圖所示。
可以看到,所有存儲引擎的源代碼都在這里。文件夾名一般就是存儲引擎的名稱,如archive、blackhole、csv、fedorated、heap、ibmdb2i、myisam、innobase。從MySQL 5.5版本開始,InnoDB Plugin已經作為默認的InnoDB存儲引擎版本;而在MySQL 5.1的源代碼中,應該可以看到兩個版本的InnoDB存儲引擎源代碼。可以看到有innobase和innodb_plugin兩個文件夾:innobase文件夾是舊的InnoDB存儲引擎的源代碼;innodb_plugin文件夾是InnoDB Plugin存儲引擎的源代碼。如果你想將InnoDB Plugin直接靜態編譯到MySQL數據庫中,那么需要刪除innobase文件夾,再將innodb_plugin文件夾重命名為innobase。
InnoDB源代碼結構
進入InnoDB存儲引擎的源代碼文件夾,可以看到源代碼結構 :
下面介紹一些主要文件夾內源代碼的具體作用:
btr:B+樹的實現。
buf:緩沖池的實現,包括LRU算法、Flush刷新算法等。
dict:InnoDB存儲引擎內存數據字典的實現。
dyn:InnoDB存儲引擎動態數組的實現。
fil:InnoDB存儲引擎中文件數據結構以及對于文件的一些操作。
fsp:你可以理解為file space,即對InnoDB存儲引擎物理文件的管理,如頁、區、段等。
ha:哈希算法的實現。
handler:繼承于MySQL的handler,插件式存儲引擎的實現。
ibuf:插入緩沖的實現。
include:InnoDB將頭文件(.h,.ic)都統一放在這個文件夾下。
lock:InnoDB存儲引擎鎖的實現,如S鎖、X鎖以及定義鎖的一系列算法。
log:日志緩沖和重組日志文件的實現。對重組日志感興趣的,應該好好閱讀該源代碼。
mem:輔助緩沖池的實現,用來申請一些數據結構的內存。
mtr:事務的底層實現。
os:封裝一些對于操作系統的操作。
page:頁的實現。
row:對于各種類型行數據的操作。
srv:對于InnoDB存儲引擎參數的設計。
sync:InnoDB存儲引擎互斥量(Mutex)的實現。
thr:InnoDB儲存引擎封裝的可移植的線程庫。
trx:事務的實現。
ut:工具類。
編譯和調試InnoDB源代碼
Windows下的調試
在Windows平臺下,可以通過Visual Studion 2003、2005和2008開發工具對MySQL的源代碼進行編譯和調試。在此之前,需要預先安裝如下的工具:
CMake:可以從http://www.cmake.org下載。
bison:可以從http://gnuwin32.sourceforge.net/packages/bison.htm下載。
安裝之后,還需要通過configure.js這個命令進行配置:
C:\workdir>win\configure.js options
option比較重要的選項如下所示。
WITH_INNOBASE_STORAGE_ENGINE:支持InnoDB存儲引擎。
WITH_PARTITION_STORAGE_ENGINE:分區支持。
WITH_ARCHIVE_STORAGE_ENGINE:支持Archive存儲引擎。
WITH_BLACKHOLE_STORAGE_ENGINE:支持Blackhole存儲引擎。
WITH_EXAMPLE_STORAGE_ENGINE:支持Example存儲引擎,這個存儲引擎是展示給開發人員的,你可以從這個存儲引擎開始構建自己的存儲引擎。
WITH_FEDERATED_STORAGE_ENGINE:支持Federated存儲引擎。
WITH_NDBCLUSTER_STORAGE_ENGINE:支持NDB Cluster存儲引擎。
如果只是比較關心InnoDB存儲引擎,可以這樣進行設置,如圖所示。
之后,可以根據你使用的是Visual Studio 2005還是Visual Studio 2008,在win文件下運行build-vsx.bat文件來生成Visual Studio的工程文件。build-vs8.bat表示Visual Studio 2005,build-vs8_x64.bat表示需要編譯64位的MySQL數據庫。如我們需要在32位的操作系統下使用Visual Studio 2008進行調試工作,則可以使用如下命令:
D:\Project\mysql-5.5.5-m3>win\build-vs9.bat
這樣就生成了MySQL.sln的工程文件,打開這個工程文件并將mysqld這個項目設置為默認的啟動項,就可以進行MySQL的編譯和調試了。
之后的編譯、斷點的設置和調試,與在Visual Studio下操作一般的程序沒有什么區別。
Linux下的調試
Linux下的調試,通常使用Eclipse。其他一些類Unix操作系統,如Solaris、FreeBSD、MAC,同樣可以使用Eclipse進行調試。
- 到http://www.eclipse.org/downloads/下載并安裝Eclipse IDE for C/C++Developers。
- 解壓MySQL源代碼到指定目錄,如解壓到/root/workspace/mysql-5.5.5-m3,
- 運行如下命令產生Make文件(Eclipse會使用產生的這些Make文件):[root mysql-5.5.5-m3]#BUILD/compile-amd64-debug-max-no-ndb-c,BUILD下有很多compile文件,你可以選擇你所需要的文件。編譯的平臺是64位的Linux系統,并且希望可以進行Debug調試,因此選擇了compile-amd64-debug-max-no-ndb文件。注意-c選項,這個選項只生產Make文件,不進行編譯。
- 接著打開Eclipse,新建一個C++的項目。給項目取個名稱,如這里的項目名為mysql_5_5_5,并選擇一個空的項目。選擇Finish按鈕后,可以看到新產生的一個空項目。
- 之后選擇左邊的Project Explorer,右擊項目mysql_5_5_5,選擇新建文件夾,將文件夾/root/workspace/mysql-5.5.5-m3導入工程中。
- 導入文件夾后,再右擊項目名mysql_5_5_5,選擇項目屬性,在C/C++Build選項這里進行設置,需要將Build directory選擇為源代碼所在路徑。 編譯配置完后,程序就會自動開始執行編譯工作了。
- 上述的這個過程只是編譯的過程,換句話說,編譯完后就產生了mysqld這樣的執行文件。如果想要進行調試,還需要在Debug這里進行如下的配置。 另外如果需要配置一些額外的參數,需要切換到Arguments選項。
- 之后就可以設置斷點,進行調試工作了,這和一般的程序并沒有什么不同。