├── README.md ├── lock.md ├── pdf ├── lock.odg ├── lock.pdf ├── mysql_innodb.odg └── mysql_innodb.pdf └── pic ├── README.md ├── lock01.bmp ├── lock01.png ├── lock02.bmp ├── lock02.png ├── lock03.png ├── lock04.png ├── lock05.png ├── lock06.png ├── lock07.png ├── lock08.png ├── lock09.png ├── lock10.png ├── lock11.png ├── lock13.png ├── lock14.png ├── lock15.png ├── lock16.png ├── lock17.png ├── lock18.png └── lock19.png /README.md: -------------------------------------------------------------------------------- 1 | # inside-innodb 2 | innodb存储引擎深入学习记录,用图片加深理解与记忆 3 | -------------------------------------------------------------------------------- /lock.md: -------------------------------------------------------------------------------- 1 | # INNODB锁 2 | 3 | [TOC] 4 | 5 | ![lock01](pic/lock01.png) 6 | 7 | ## 设置INNODB事务隔离级别 8 | 9 | ### 实践1:查看innodb默认的事务隔离级别 10 | 11 | 知识点: 12 | 13 | 1. 可以查看局部变量`@@tx_isolation`和全局变量`@@global.tx_isolation` 14 | 2. 局部变量在会话中生效,而全局变量是在所有会话中生效,局部覆盖全局。 15 | 16 | >查看当前会话中的事务隔离级别 17 | 18 | ```shell 19 | MariaDB [(none)]> select @@tx_isolation; 20 | +-----------------+ 21 | | @@tx_isolation | 22 | +-----------------+ 23 | | REPEATABLE-READ | 24 | +-----------------+ 25 | 1 row in set (0.00 sec) 26 | ``` 27 | 28 | 查看全局的事务隔离级别 29 | 30 | ```shell 31 | MariaDB [(none)]> select @@global.tx_isolation; 32 | +-----------------------+ 33 | | @@global.tx_isolation | 34 | +-----------------------+ 35 | | REPEATABLE-READ | 36 | +-----------------------+ 37 | 1 row in set (0.00 sec) 38 | 39 | MariaDB [(none)]> show variables like "tx_isolation"; 40 | +---------------+-----------------+ 41 | | Variable_name | Value | 42 | +---------------+-----------------+ 43 | | tx_isolation | REPEATABLE-READ | 44 | +---------------+-----------------+ 45 | 1 row in set (0.00 sec) 46 | 47 | MariaDB [(none)]> select * from information_schema.global_variables where variable_name like "%isolation%"; 48 | +---------------+-----------------+ 49 | | VARIABLE_NAME | VARIABLE_VALUE | 50 | +---------------+-----------------+ 51 | | TX_ISOLATION | REPEATABLE-READ | 52 | +---------------+-----------------+ 53 | 1 row in set (0.03 sec) 54 | ``` 55 | 56 | ### 实践2:改变单个会话的隔离级别 57 | 58 | 59 | 知识点: 60 | 61 | 1.用户可以用SET TRANSACTION语句改变单个会话的隔离级别。 62 | 63 | ```shell 64 | SET SESSION TRANSACTION ISOLATION LEVEL 65 | {READ UNCOMMITTED | READ COMMITTED 66 | | REPEATABLE READ | SERIALIZABLE} 67 | ``` 68 | 69 | 2.会话结束重新开启新的会话,则使用全局变量的值 70 | 71 | 72 | 73 | session1设置RR 74 | 75 | 由于默认的隔离级别就是RR,因此不用设置,查看一下即可 76 | 77 | ```shell 78 | MariaDB [(none)]> select @@tx_isolation; 79 | +-----------------+ 80 | | @@tx_isolation | 81 | +-----------------+ 82 | | REPEATABLE-READ | 83 | +-----------------+ 84 | 1 row in set (0.00 sec) 85 | ``` 86 | 87 | session2设置RC 88 | 89 | ```shell 90 | MariaDB [information_schema]> select @@tx_isolation; 91 | +-----------------+ 92 | | @@tx_isolation | 93 | +-----------------+ 94 | | REPEATABLE-READ | 95 | +-----------------+ 96 | 1 row in set (0.00 sec) 97 | 98 | MariaDB [information_schema]> set session transaction isolation level read committed; 99 | Query OK, 0 rows affected (0.00 sec) 100 | 101 | MariaDB [information_schema]> select @@tx_isolation; 102 | +----------------+ 103 | | @@tx_isolation | 104 | +----------------+ 105 | | READ-COMMITTED | 106 | +----------------+ 107 | 1 row in set (0.00 sec) 108 | ``` 109 | 110 | session2结束会话,开启新的session3 111 | 112 | ```shell 113 | MariaDB [information_schema]> exit 114 | Bye 115 | [root@localhost ~]# mysql 116 | Welcome to the MariaDB monitor. Commands end with ; or \g. 117 | Your MariaDB connection id is 6 118 | Server version: 5.5.44-MariaDB MariaDB Server 119 | 120 | Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others. 121 | 122 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 123 | 124 | MariaDB [(none)]> select @@tx_isolation; 125 | +-----------------+ 126 | | @@tx_isolation | 127 | +-----------------+ 128 | | REPEATABLE-READ | 129 | +-----------------+ 130 | 1 row in set (0.00 sec) 131 | ``` 132 | 133 | ### 实践3:改变单个实例的隔离级别 134 | 135 | 知识点: 136 | 137 | 1.用户可以用SET TRANSACTION语句改变单个实例的隔离级别。 138 | 139 | ```shell 140 | SET GLOBAL TRANSACTION ISOLATION LEVEL 141 | {READ UNCOMMITTED | READ COMMITTED 142 | | REPEATABLE READ | SERIALIZABLE} 143 | ``` 144 | 145 | 2.实例结束重新开启新的实例,则使用配置文件中的参数值,或程序编译时的参数值。 146 | 147 | 148 | ```shell 149 | MariaDB [(none)]> set global transaction isolation level read committed; 150 | Query OK, 0 rows affected (0.00 sec) 151 | 152 | MariaDB [(none)]> select @@tx_isolation; 153 | +-----------------+ 154 | | @@tx_isolation | 155 | +-----------------+ 156 | | REPEATABLE-READ | 157 | +-----------------+ 158 | 1 row in set (0.00 sec) 159 | 160 | MariaDB [(none)]> select @@global.tx_isolation; 161 | +-----------------------+ 162 | | @@global.tx_isolation | 163 | +-----------------------+ 164 | | READ-COMMITTED | 165 | +-----------------------+ 166 | 1 row in set (0.00 sec). 167 | ``` 168 | 169 | 170 | 当前会话中,局部变量的值为RR,全局变量的值为RC,而局部会覆盖全局,所以当前会话中的隔离级别还是RR,我们需要退出当前会话,开启新的会话。 171 | 172 | 173 | ```shell 174 | [root@localhost ~]# mysql 175 | Welcome to the MariaDB monitor. Commands end with ; or \g. 176 | Your MariaDB connection id is 8 177 | Server version: 5.5.44-MariaDB MariaDB Server 178 | 179 | Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others. 180 | 181 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 182 | 183 | MariaDB [(none)]> select @@tx_isolation; 184 | +----------------+ 185 | | @@tx_isolation | 186 | +----------------+ 187 | | READ-COMMITTED | 188 | +----------------+ 189 | 1 row in set (0.00 sec) 190 | 191 | MariaDB [(none)]> select @@global.tx_isolation; 192 | +-----------------------+ 193 | | @@global.tx_isolation | 194 | +-----------------------+ 195 | | READ-COMMITTED | 196 | +-----------------------+ 197 | 1 row in set (0.00 sec) 198 | ``` 199 | 200 | 在会话中通过修改全局变量的方式,只能让当前的实例生效,如果服务重启了,则失效。 201 | 202 | ```shell 203 | [root@localhost ~]# systemctl restart mariadb 204 | [root@localhost ~]# mysql 205 | Welcome to the MariaDB monitor. Commands end with ; or \g. 206 | Your MariaDB connection id is 2 207 | Server version: 5.5.44-MariaDB MariaDB Server 208 | 209 | Copyright (c) 2000, 2015, Oracle, MariaDB Corporation Ab and others. 210 | 211 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 212 | 213 | MariaDB [(none)]> select @@tx_isolation; 214 | +-----------------+ 215 | | @@tx_isolation | 216 | +-----------------+ 217 | | REPEATABLE-READ | 218 | +-----------------+ 219 | 1 row in set (0.00 sec) 220 | 221 | MariaDB [(none)]> select @@global.tx_isolation; 222 | +-----------------------+ 223 | | @@global.tx_isolation | 224 | +-----------------------+ 225 | | REPEATABLE-READ | 226 | +-----------------------+ 227 | 1 row in set (0.00 sec) 228 | ``` 229 | 230 | ### 实践4:改变所有实例的隔离级别 231 | 232 | 知识点: 233 | 234 | 1.修改配置文件,为所有实例和连接设置默认隔离级别。 235 | 236 | ```shell 237 | [mysqld] 238 | transaction-isolation = {READ-UNCOMMITTED | READ-COMMITTED 239 | | REPEATABLE-READ | SERIALIZABLE} 240 | ``` 241 | 242 | 2.innodb默认的隔离级别为 REPEATABLE-READ 243 | 244 | ```shell 245 | [root@localhost ~]# vim /etc/my.cnf 246 | transaction-isolation=read-committed 247 | [root@localhost ~]# systemctl restart mariadb 248 | [root@localhost ~]# mysql -e "select @@tx_isolation" 249 | +----------------+ 250 | | @@tx_isolation | 251 | +----------------+ 252 | | READ-COMMITTED | 253 | +----------------+ 254 | [root@localhost ~]# mysql -e "select @@global.tx_isolation" 255 | +-----------------------+ 256 | | @@global.tx_isolation | 257 | +-----------------------+ 258 | | READ-COMMITTED | 259 | +-----------------------+ 260 | ``` 261 | 262 | --- 263 | 264 | ## 区分INNODB事务隔离级别 265 | 266 | ![lock02](pic/lock02.png) 267 | 268 | ### InnoDB 中的隔离级详细描述 269 | 270 | - `READ UNCOMMITTED` 这通常称为 'dirty read':non-locking SELECTs 的执行使我们不会看到一个记录的可能更早的版本;因而在这个隔离度下是非 'consistent' reads;另外,这级隔离的运作如同 READ COMMITTED。 271 | - `READ COMMITTED` 有些类似 Oracle 的隔离级。所有 SELECT ... FOR UPDATE 和 SELECT ... LOCK IN SHARE MODE 语句只锁定索引记录,而不锁定之前的间隙,因而允许在锁定的记录后自由地插入新记录。以一个唯一地搜索条件使用一个唯一索引(unique index)的 UPDATE 和 DELETE,仅仅只锁定所找到的索引记录,而不锁定该索引之前的间隙。但是在范围型的 UPDATE and DELETE中,InnoDB 必须设置 next-key 或 gap locks 来阻塞其它用户对范围内的空隙插入。 自从为了 MySQL 进行复制(replication)与恢复(recovery)工作'phantom rows'必须被阻塞以来,这就是必须的了。Consistent reads 运作方式与 Oracle 有点类似: 每一个 consistent read,甚至是同一个事务中的,均设置并作用它自己的最新快照。 272 | - `REPEATABLE READ` 这是 InnoDB 默认的事务隔离级。. SELECT ... FOR UPDATE, SELECT ... LOCK IN SHARE MODE, UPDATE, 和 DELETE ,这些以唯一条件搜索唯一索引的,只锁定所找到的索引记录,而不锁定该索引之前的间隙。 否则这些操作将使用 next-key 锁定,以 next-key 和 gap locks 锁定找到的索引范围,并阻塞其它用户的新建插入。在 consistent reads 中,与前一个隔离级相比这是一个重要的差别: 在这一级中,同一事务中所有的 consistent reads 均读取第一次读取时已确定的快照。这个约定就意味着如果在同一事务中发出几个无格式(plain)的SELECTs ,这些 SELECTs 的相互关系是一致的。 273 | - `SERIALIZABLE` 这一级与上一级相似,只是无格式(plain)的 SELECTs 被隐含地转换为 SELECT ... LOCK IN SHARE MODE。 274 | 275 | ### 实践1:SERIALIZABLE隔离级别查询自动加共享锁 276 | 277 | * 开启四个会话session1-4,分别设置不同的隔离级别 278 | 279 | ![lock03](pic/lock03.png) 280 | 281 | * 再开启一个会话session5默认使用RR隔离级别 282 | 283 | ![lock04](pic/lock04.png) 284 | 285 | * session1-5都开启一个事务,查看test库中t1表中id=100的行 286 | 287 | ![lock05](pic/lock05.png) 288 | 289 | * session5中将id=100的行改为200,发现出现死锁,这是因为session4为SERIALIZABLE,查看id=100的行会被加上一个共享锁S,而其他三种模式都是不加锁的,使用一致性非锁定读。 290 | 291 | ![lock06](pic/lock06.png) 292 | 293 | ```shell 294 | MariaDB [(none)]> select * from information_schema.innodb_locks\G; 295 | *************************** 1. row *************************** 296 | lock_id: 909:0:308:2 297 | lock_trx_id: 909 298 | lock_mode: X 299 | lock_type: RECORD 300 | lock_table: `test`.`t1` 301 | lock_index: `PRIMARY` 302 | lock_space: 0 303 | lock_page: 308 304 | lock_rec: 2 305 | lock_data: 100 306 | *************************** 2. row *************************** 307 | lock_id: 90A:0:308:2 308 | lock_trx_id: 90A 309 | lock_mode: S 310 | lock_type: RECORD 311 | lock_table: `test`.`t1` 312 | lock_index: `PRIMARY` 313 | lock_space: 0 314 | lock_page: 308 315 | lock_rec: 2 316 | lock_data: 100 317 | 2 rows in set (0.01 sec) 318 | 319 | ERROR: No query specified 320 | 321 | MariaDB [(none)]> select * from information_schema.innodb_lock_waits\G; 322 | *************************** 1. row *************************** 323 | requesting_trx_id: 909 324 | requested_lock_id: 909:0:308:2 325 | blocking_trx_id: 90A 326 | blocking_lock_id: 90A:0:308:2 327 | 1 row in set (0.04 sec) 328 | 329 | ERROR: No query specified 330 | 331 | MariaDB [(none)]> select * from information_schema.innodb_trx\G; 332 | *************************** 1. row *************************** 333 | trx_id: 90A 334 | trx_state: RUNNING 335 | trx_started: 2016-12-15 17:08:01 336 | trx_requested_lock_id: NULL 337 | trx_wait_started: NULL 338 | trx_weight: 2 339 | trx_mysql_thread_id: 5 340 | trx_query: NULL 341 | trx_operation_state: NULL 342 | trx_tables_in_use: 0 343 | trx_tables_locked: 0 344 | trx_lock_structs: 2 345 | trx_lock_memory_bytes: 376 346 | trx_rows_locked: 1 347 | trx_rows_modified: 0 348 | trx_concurrency_tickets: 0 349 | trx_isolation_level: SERIALIZABLE 350 | trx_unique_checks: 1 351 | trx_foreign_key_checks: 1 352 | trx_last_foreign_key_error: NULL 353 | trx_adaptive_hash_latched: 0 354 | trx_adaptive_hash_timeout: 10000 355 | *************************** 2. row *************************** 356 | trx_id: 909 357 | trx_state: LOCK WAIT 358 | trx_started: 2016-12-15 17:02:04 359 | trx_requested_lock_id: 909:0:308:2 360 | trx_wait_started: 2016-12-15 17:10:18 361 | trx_weight: 2 362 | trx_mysql_thread_id: 4 363 | trx_query: update test.t1 set id=200 where id=100 364 | trx_operation_state: starting index read 365 | trx_tables_in_use: 1 366 | trx_tables_locked: 1 367 | trx_lock_structs: 2 368 | trx_lock_memory_bytes: 1248 369 | trx_rows_locked: 1 370 | trx_rows_modified: 0 371 | trx_concurrency_tickets: 0 372 | trx_isolation_level: REPEATABLE READ 373 | trx_unique_checks: 1 374 | trx_foreign_key_checks: 1 375 | trx_last_foreign_key_error: NULL 376 | trx_adaptive_hash_latched: 0 377 | trx_adaptive_hash_timeout: 10000 378 | *************************** 3. row *************************** 379 | trx_id: 908 380 | trx_state: RUNNING 381 | trx_started: 2016-12-15 17:02:02 382 | trx_requested_lock_id: NULL 383 | trx_wait_started: NULL 384 | trx_weight: 0 385 | trx_mysql_thread_id: 3 386 | trx_query: NULL 387 | trx_operation_state: NULL 388 | trx_tables_in_use: 0 389 | trx_tables_locked: 0 390 | trx_lock_structs: 0 391 | trx_lock_memory_bytes: 376 392 | trx_rows_locked: 0 393 | trx_rows_modified: 0 394 | trx_concurrency_tickets: 0 395 | trx_isolation_level: READ COMMITTED 396 | trx_unique_checks: 1 397 | trx_foreign_key_checks: 1 398 | trx_last_foreign_key_error: NULL 399 | trx_adaptive_hash_latched: 0 400 | trx_adaptive_hash_timeout: 10000 401 | *************************** 4. row *************************** 402 | trx_id: 906 403 | trx_state: RUNNING 404 | trx_started: 2016-12-15 17:01:57 405 | trx_requested_lock_id: NULL 406 | trx_wait_started: NULL 407 | trx_weight: 0 408 | trx_mysql_thread_id: 6 409 | trx_query: NULL 410 | trx_operation_state: NULL 411 | trx_tables_in_use: 0 412 | trx_tables_locked: 0 413 | trx_lock_structs: 0 414 | trx_lock_memory_bytes: 376 415 | trx_rows_locked: 0 416 | trx_rows_modified: 0 417 | trx_concurrency_tickets: 0 418 | trx_isolation_level: REPEATABLE READ 419 | trx_unique_checks: 1 420 | trx_foreign_key_checks: 1 421 | trx_last_foreign_key_error: NULL 422 | trx_adaptive_hash_latched: 0 423 | trx_adaptive_hash_timeout: 10000 424 | *************************** 5. row *************************** 425 | trx_id: 905 426 | trx_state: RUNNING 427 | trx_started: 2016-12-15 17:01:39 428 | trx_requested_lock_id: NULL 429 | trx_wait_started: NULL 430 | trx_weight: 0 431 | trx_mysql_thread_id: 2 432 | trx_query: select * from information_schema.innodb_trx 433 | trx_operation_state: NULL 434 | trx_tables_in_use: 0 435 | trx_tables_locked: 0 436 | trx_lock_structs: 0 437 | trx_lock_memory_bytes: 376 438 | trx_rows_locked: 0 439 | trx_rows_modified: 0 440 | trx_concurrency_tickets: 0 441 | trx_isolation_level: READ UNCOMMITTED 442 | trx_unique_checks: 1 443 | trx_foreign_key_checks: 1 444 | trx_last_foreign_key_error: NULL 445 | trx_adaptive_hash_latched: 0 446 | trx_adaptive_hash_timeout: 10000 447 | 5 rows in set (0.00 sec) 448 | 449 | ERROR: No query specified 450 | 451 | ``` 452 | 453 | * session4中提交事务,则id=100的行锁被解除,我们关闭session4,下图为最新的情况 454 | 455 | ![lock07](pic/lock07.png) 456 | 457 | ### 实践2:RU、RC、RR隔离级别的对比 458 | 459 | * session5,修改id=100的行,改为200,不提交事务,session1-,3分别查看id=100的值,观察情况 460 | 461 | ![lock08](pic/lock08.png) 462 | 463 | * RU级别的会话中的事务在session5中事务未提交的情况下,就能够查看到最新的行记录了 464 | 465 | ![lock09](pic/lock09.png) 466 | 467 | * RC级别的会话中的事务在session5会话的事务提交后就能够查看到最新的行记录了 468 | 469 | ![lock10](pic/lock10.png) 470 | 471 | * RR级别的会话中必须在session5的事务提交后并且自己的事务也提交后才能查到最新的行记录 472 | 473 | 474 | ## 实现一致性锁定读 475 | 476 | InnoDB默认是可重复读的(REPEATABLE READ),MVCC多版本并发控制,实现一致性地非锁定读操作。 477 | 478 | InnoDB存储引擎的select操作使用一致性非锁定读;也就是说,select操作不会去请求共享锁S; 479 | 480 | 如何显示地使用一致性锁定读呢? 481 | 482 | * 第一种方法,显式地加共享锁S:select * from t1 where id=1 lock on share mode; 483 | * 第二种方法,显式地加排他锁X:select * from t1 where id=1 for update; 484 | 485 | ### 实践1:设置innodb申请锁等待超时时间 486 | 487 | ![lock13](pic/lock13.png) 488 | 489 | ```shell 490 | MariaDB [(none)]> set @@innodb_lock_wait_timeout=3; 491 | Query OK, 0 rows affected (0.01 sec) 492 | ``` 493 | 494 | ### 实践2:设置一致性锁定读,加共享锁测试 495 | 496 | ![lock16](pic/lock16.png) 497 | 498 | 打开两个会话,分别按照图片中去做测试 499 | 500 | ![lock14](pic/lock14.png) 501 | 502 | 从实践中可以得到以下信息: 503 | 504 | * 事务A对id=1的行申请了共享S锁之后,事务B要么使用一致性非锁定读,即不请求锁,或者使用一致性锁定读的共享锁,即请求共享S锁 505 | * 而事务B中需要请求排他锁的写操作都不能执行,每次都是锁请求等待超时 506 | 507 | 508 | ### 实践3:设置一致性锁定读,加排他锁测试 509 | 510 | ![lock17](pic/lock17.png) 511 | 512 | 这一次事务A以及对id=1的行申请了排他锁X,按照下图做测试: 513 | 514 | ![lock15](pic/lock15.png) 515 | 516 | 从实践中可以得到以下信息: 517 | 518 | * 事务A对id=1的行申请了排他锁X之后,事务B只能使用一致性非锁定读,即不请求锁 519 | * 而事务B中需要请求锁的行为都会等待超时,包括排他锁的写操作和共享锁的读操作都不能执行 520 | 521 | 522 | ## 认识锁的算法 523 | 524 | nnoDB存储引擎的锁的算法有三种: 525 | 526 | * Record lock:单个行记录上的锁 527 | * Gap lock:间隙锁,锁定一个范围,不包括记录本身 528 | * Next-key lock:record+gap 锁定一个范围,包含记录本身 529 | 530 | Lock的精度(type)分为 行锁、表锁、意向锁 531 | 532 | Lock的模式(mode)分为: 533 | 534 | * 锁的类型 ——【读锁和写锁】或者【共享锁和排他锁】即 【X or S】 535 | * 锁的范围 ——【record lock、gap lock、Next-key lock】 536 | 537 | ### 知识点 538 | 539 | 1. innodb对于行的查询使用next-key lock 540 | 2. Next-locking keying为了解决Phantom Problem幻读问题 541 | 3. 当查询的索引含有唯一属性时,将next-key lock降级为record key 542 | 4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生 543 | 5. 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) 544 | A. 将事务隔离级别设置为RC 545 | B. 将参数innodb_locks_unsafe_for_binlog设置为1 546 | 547 | ![lock18](pic/lock18.png) 548 | 549 | ### 实践1: 验证next-key lock降级为record key 550 | 551 | 创建db1.t1表,有列a和b,分别为char(10)和int型,并且b为key,注意b列为索引列,但并不是主键,因此不是唯一的。 552 | 553 | ```shell 554 | MariaDB [db1]> create table db1.t1 (a char(10),b int,key (b)); 555 | Query OK, 0 rows affected (0.03 sec) 556 | 557 | MariaDB [db1]> insert into db1.t1 values ('batman',1),('superman',3),('leo',5); 558 | Query OK, 3 rows affected (0.15 sec) 559 | Records: 3 Duplicates: 0 Warnings: 0 560 | 561 | MariaDB [db1]> select * from db1.t1; 562 | +----------+------+ 563 | | a | b | 564 | +----------+------+ 565 | | batman | 1 | 566 | | superman | 3 | 567 | | leo | 5 | 568 | +----------+------+ 569 | 3 rows in set (0.02 sec) 570 | ``` 571 | 572 | 接下来开启两个事务T1和T2,T1中查看b=3的行,显式加排他锁;T1未提交事务时,T2事务开启并尝试插入新行a='batman',b=2和a='batman',b=4; 573 | 574 | ```shell 575 | #事务T1 576 | MariaDB [db1]> begin; 577 | Query OK, 0 rows affected (0.00 sec) 578 | 579 | MariaDB [db1]> select * from db1.t1 where b=3 for update; 580 | +----------+------+ 581 | | a | b | 582 | +----------+------+ 583 | | superman | 3 | 584 | +----------+------+ 585 | 1 row in set (0.12 sec) 586 | 587 | #事务T2 588 | MariaDB [db1]> begin; 589 | Query OK, 0 rows affected (0.00 sec) 590 | 591 | MariaDB [db1]> insert into db1.t1 values ('batman',2); 592 | ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 593 | MariaDB [db1]> insert into db1.t1 values ('batman',4); 594 | ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 595 | ``` 596 | 597 | 发现T2事务中不能插入新行a='batman',b=2和a='batman',b=4;可以查看当前innodb锁的信息 598 | 599 | ```shell 600 | MariaDB [db1]> select * from information_schema.innodb_locks\G; 601 | *************************** 1. row *************************** 602 | lock_id: 111B:0:334:3 603 | lock_trx_id: 111B 604 | lock_mode: X,GAP 605 | lock_type: RECORD 606 | lock_table: `db1`.`t1` 607 | lock_index: `b` 608 | lock_space: 0 609 | lock_page: 334 610 | lock_rec: 3 611 | lock_data: 3, 0x00000000020E 612 | *************************** 2. row *************************** 613 | lock_id: 111A:0:334:3 614 | lock_trx_id: 111A 615 | lock_mode: X 616 | lock_type: RECORD 617 | lock_table: `db1`.`t1` 618 | lock_index: `b` 619 | lock_space: 0 620 | lock_page: 334 621 | lock_rec: 3 622 | lock_data: 3, 0x00000000020E 623 | 2 rows in set (0.01 sec) 624 | 625 | ERROR: No query specified 626 | 627 | MariaDB [db1]> select * from information_schema.innodb_lock_waits\G; 628 | *************************** 1. row *************************** 629 | requesting_trx_id: 111B 630 | requested_lock_id: 111B:0:334:3 631 | blocking_trx_id: 111A 632 | blocking_lock_id: 111A:0:334:3 633 | 1 row in set (0.09 sec) 634 | 635 | MariaDB [db1]> select * from information_schema.innodb_lock_waits\G; 636 | *************************** 1. row *************************** 637 | requesting_trx_id: 111B 638 | requested_lock_id: 111B:0:334:4 639 | blocking_trx_id: 111A 640 | blocking_lock_id: 111A:0:334:4 641 | 1 row in set (0.00 sec) 642 | 643 | ERROR: No query specified 644 | 645 | MariaDB [db1]> select * from information_schema.innodb_locks\G; 646 | *************************** 1. row *************************** 647 | lock_id: 111B:0:334:4 648 | lock_trx_id: 111B 649 | lock_mode: X,GAP 650 | lock_type: RECORD 651 | lock_table: `db1`.`t1` 652 | lock_index: `b` 653 | lock_space: 0 654 | lock_page: 334 655 | lock_rec: 4 656 | lock_data: 5, 0x00000000020F 657 | *************************** 2. row *************************** 658 | lock_id: 111A:0:334:4 659 | lock_trx_id: 111A 660 | lock_mode: X,GAP 661 | lock_type: RECORD 662 | lock_table: `db1`.`t1` 663 | lock_index: `b` 664 | lock_space: 0 665 | lock_page: 334 666 | lock_rec: 4 667 | lock_data: 5, 0x00000000020F 668 | 2 rows in set (0.11 sec) 669 | 670 | ERROR: No query specified 671 | ``` 672 | 673 | 我们看到T2事务的两次插入动作都在请求排他锁,但是此时T1事务已经在加了next-key lock(record + gap),表现范围为b的(1,5),包括记录3,所以T2事务在T1事务解锁之间,不能插入到b的(1,5)范围内 674 | 675 | × `lock_mode: X,GAP` lock_mode 可以理解为 `读锁还是写锁?`;`是在什么范围上锁?`;此处加的写锁即排他锁;范围是(1,5) 676 | * `lock_type: RECORD` 表示锁的精度,根据存储引擎不同,innodb是行锁,MYISAM是表锁 677 | 678 | 删除db1.t1表,重新创建db1.t1表,有列a和b,分别为char(10)和int型,并且b为primay key,因此b列是唯一的。 679 | 680 | ```shell 681 | MariaDB [db1]> drop tables t1; 682 | Query OK, 0 rows affected (0.12 sec) 683 | 684 | MariaDB [db1]> create table db1.t1 (a char(10),b int ,primary key (b)); 685 | Query OK, 0 rows affected (0.02 sec) 686 | 687 | MariaDB [db1]> insert into db1.t1 values ('batman',1),('superman',3),('leo',5); 688 | Query OK, 3 rows affected (0.12 sec) 689 | Records: 3 Duplicates: 0 Warnings: 0 690 | 691 | MariaDB [db1]> select * from db1.t1; 692 | +----------+---+ 693 | | a | b | 694 | +----------+---+ 695 | | batman | 1 | 696 | | superman | 3 | 697 | | leo | 5 | 698 | +----------+---+ 699 | 3 rows in set (0.08 sec) 700 | ``` 701 | 702 | 接下来开启两个事务T1和T2,T1中查看b=3的行,显式加排他锁;T1未提交事务时,T2事务开启并尝试插入新行a='batman',b=2和a='batman',b=4; 703 | 704 | ```shell 705 | #事务T1 706 | MariaDB [db1]> begin; 707 | Query OK, 0 rows affected (0.00 sec) 708 | 709 | MariaDB [db1]> select * from db1.t1 where b=3 for update; 710 | +----------+---+ 711 | | a | b | 712 | +----------+---+ 713 | | superman | 3 | 714 | +----------+---+ 715 | 1 row in set (0.14 sec) 716 | 717 | #事务T2 718 | MariaDB [db1]> begin; 719 | Query OK, 0 rows affected (0.00 sec) 720 | 721 | MariaDB [db1]> insert into db1.t1 values ('batman',2); 722 | Query OK, 1 row affected (0.00 sec) 723 | 724 | MariaDB [db1]> insert into db1.t1 values ('batman',4); 725 | Query OK, 1 row affected (0.00 sec) 726 | ``` 727 | 728 | 继续在T2事务中尝试查看b=3的行,显式加共享锁。 729 | 730 | ```shell 731 | #事务T2 732 | MariaDB [db1]> select * from db1.t1 where b=3 lock in share mode; 733 | ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 734 | ``` 735 | 736 | 发现T2事务中可以插入新行a='batman',b=2和a='batman',b=4;但是不能查看b=3的行,接下来我们查看当前innodb锁的信息 737 | 738 | ```shell 739 | MariaDB [db1]> select * from information_schema.innodb_locks\G; 740 | *************************** 1. row *************************** 741 | lock_id: 1122:0:337:3 742 | lock_trx_id: 1122 743 | lock_mode: S 744 | lock_type: RECORD 745 | lock_table: `db1`.`t1` 746 | lock_index: `PRIMARY` 747 | lock_space: 0 748 | lock_page: 337 749 | lock_rec: 3 750 | lock_data: 3 751 | *************************** 2. row *************************** 752 | lock_id: 1121:0:337:3 753 | lock_trx_id: 1121 754 | lock_mode: X 755 | lock_type: RECORD 756 | lock_table: `db1`.`t1` 757 | lock_index: `PRIMARY` 758 | lock_space: 0 759 | lock_page: 337 760 | lock_rec: 3 761 | lock_data: 3 762 | 2 rows in set (0.02 sec) 763 | 764 | ERROR: No query specified 765 | 766 | MariaDB [db1]> select * from information_schema.innodb_lock_waits\G; 767 | *************************** 1. row *************************** 768 | requesting_trx_id: 1122 769 | requested_lock_id: 1122:0:337:3 770 | blocking_trx_id: 1121 771 | blocking_lock_id: 1121:0:337:3 772 | 1 row in set (0.00 sec) 773 | 774 | ERROR: No query specified 775 | ``` 776 | 777 | 从以上信息可以看到,T1事务当前只在b=3所在的行上加了写锁,排他锁,并没有同时使用gap锁来组成next-key lock。 778 | 779 | 到此,已经证明了,当查询的索引含有唯一属性时,将next-key lock降级为record key 780 | 781 | 我们第二次创建的t1表的列b是主键,而主键必须是唯一的。 782 | 783 | 784 | ### 实践2: 关闭GAP锁_RC 785 | 786 | 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) 787 | 788 | A. 将事务隔离级别设置为RC 789 | B. 将参数innodb_locks_unsafe_for_binlog设置为1 790 | 791 | |T1 RR|T2 RR| 792 | |:--|:--| 793 | |begin;|begin;| 794 | |select * from db1.t1 where b=3 for update;|| 795 | ||insert into db1.t1 values ('batman',2)| 796 | ||ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction| 797 | ||set session transaction isolation level READ COMMITTED;| 798 | |commit;|commit;| 799 | 800 | 注意,将T1事务设置为RC后,需要将二进制日志的格式改为row格式,否则执行显式加锁时会报错 801 | 802 | ```shell 803 | MariaDB [db1]> insert into t1 values ('batman',2); 804 | ERROR 1665 (HY000): Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED. 805 | ``` 806 | 807 | 808 | |T1 RC|T2 RR| 809 | |:--|:--| 810 | |begin;|begin;| 811 | |set session transaction isolation level READ COMMITTED;|| 812 | |select * from db1.t1 where b=3 for update;|| 813 | ||insert into db1.t1 values ('batman',2)| 814 | ||insert into db1.t1 values ('batman',4)| 815 | |commit;|commit;| 816 | 817 | 818 | ```shell 819 | #T1事务 820 | MariaDB [db1]> set session transaction isolation level READ COMMITTED; 821 | Query OK, 0 rows affected (0.00 sec) 822 | 823 | MariaDB [db1]> select @@tx_isolation; 824 | +----------------+ 825 | | @@tx_isolation | 826 | +----------------+ 827 | | READ-COMMITTED | 828 | +----------------+ 829 | 1 row in set (0.00 sec) 830 | 831 | MariaDB [db1]> begin; 832 | Query OK, 0 rows affected (0.09 sec) 833 | 834 | MariaDB [db1]> select * from t1 where b=3 for update; 835 | +----------+------+ 836 | | a | b | 837 | +----------+------+ 838 | | superman | 3 | 839 | +----------+------+ 840 | 1 row in set (0.00 sec) 841 | 842 | #T2事务 843 | MariaDB [db1]> begin; 844 | Query OK, 0 rows affected (0.16 sec) 845 | 846 | MariaDB [db1]> select @@tx_isolation; 847 | +----------------+ 848 | | @@tx_isolation | 849 | +----------------+ 850 | | READ-COMMITTED | 851 | +----------------+ 852 | 1 row in set (0.00 sec) 853 | 854 | MariaDB [db1]> insert into db1.t1 values ('batman',2); 855 | Query OK, 1 row affected (0.00 sec) 856 | 857 | MariaDB [db1]> commit; 858 | Query OK, 0 rows affected (0.01 sec) 859 | 860 | MariaDB [db1]> set session transaction isolation level REPEATABLE READ; 861 | Query OK, 0 rows affected (0.00 sec) 862 | 863 | MariaDB [db1]> select @@tx_isolation; 864 | +-----------------+ 865 | | @@tx_isolation | 866 | +-----------------+ 867 | | REPEATABLE-READ | 868 | +-----------------+ 869 | 1 row in set (0.00 sec) 870 | 871 | MariaDB [db1]> begin; 872 | Query OK, 0 rows affected (0.00 sec) 873 | 874 | MariaDB [db1]> insert into db1.t1 values ('batman',4); 875 | Query OK, 1 row affected (0.00 sec) 876 | 877 | MariaDB [db1]> commit; 878 | Query OK, 0 rows affected (0.00 sec) 879 | 880 | #T1事务 881 | MariaDB [db1]> commit; 882 | Query OK, 0 rows affected (0.00 sec) 883 | ``` 884 | 885 | 我在做测试的时候,T1事务隔离界别为RC,T2事务的隔离界别分别用RC和RR做了测试,都是可以的 886 | 887 | ### 实践3: 关闭GAP锁_innodb_locks_unsafe_for_binlog 888 | 889 | 890 | 查看当前innodb_locks_unsafe_for_binlog参数的值 891 | 892 | ```shell 893 | MariaDB [(none)]> select @@innodb_locks_unsafe_for_binlog; 894 | +----------------------------------+ 895 | | @@innodb_locks_unsafe_for_binlog | 896 | +----------------------------------+ 897 | | 0 | 898 | +----------------------------------+ 899 | 1 row in set (0.00 sec) 900 | ``` 901 | 902 | 修改参数,并重新启动服务 903 | 904 | ```shell 905 | [root@localhost ~]# vim /etc/my.cnf 906 | innodb_locks_unsafe_for_binlog=1 907 | [root@localhost ~]# systemctl restart mariadb 908 | 909 | [root@localhost ~]# mysql -e "select @@innodb_locks_unsafe_for_binlog" 910 | +----------------------------------+ 911 | | @@innodb_locks_unsafe_for_binlog | 912 | +----------------------------------+ 913 | | 1 | 914 | +----------------------------------+ 915 | ``` 916 | 917 | 还是去创建db1.t1表,如果已有就先drop;有列a和b,分别为char(10)和int型,并且b为key,注意b列为索引列,但并不是主键,因此不是唯一的。 918 | 919 | 920 | |T1 RR|T2 RR| 921 | |:--|:--| 922 | |begin;|begin;| 923 | |select * from db1.t1 where b=3 for update;|| 924 | ||insert into db1.t1 values ('batman',2)| 925 | ||insert into db1.t1 values ('batman',4)| 926 | |commit;|commit;| 927 | 928 | ```shell 929 | MariaDB [db1]> create table db1.t1 (a char(10),b int,key (b)); 930 | Query OK, 0 rows affected (0.03 sec) 931 | 932 | MariaDB [db1]> insert into db1.t1 values ('batman',1),('superman',3),('leo',5); 933 | Query OK, 3 rows affected (0.15 sec) 934 | Records: 3 Duplicates: 0 Warnings: 0 935 | 936 | MariaDB [db1]> select * from db1.t1; 937 | +----------+------+ 938 | | a | b | 939 | +----------+------+ 940 | | batman | 1 | 941 | | superman | 3 | 942 | | leo | 5 | 943 | +----------+------+ 944 | 3 rows in set (0.02 sec) 945 | ``` 946 | 947 | 接下来开启两个事务T1和T2,T1中查看b=3的行,显式加排他锁;T1未提交事务时,T2事务开启并尝试插入新行a='batman',b=2和a='batman',b=4; 948 | 949 | T1事务 950 | 951 | ```shell 952 | MariaDB [(none)]> begin; 953 | Query OK, 0 rows affected (0.00 sec) 954 | 955 | MariaDB [(none)]> select * from db1.t1 where b=3 for update; 956 | +----------+------+ 957 | | a | b | 958 | +----------+------+ 959 | | superman | 3 | 960 | +----------+------+ 961 | 1 row in set (0.01 sec) 962 | ``` 963 | 964 | T2事务 965 | 966 | ```shell 967 | MariaDB [(none)]> begin; 968 | Query OK, 0 rows affected (0.00 sec) 969 | 970 | MariaDB [(none)]> insert into db1.t1 values ('batman',4); 971 | Query OK, 1 row affected (0.01 sec) 972 | 973 | MariaDB [(none)]> insert into db1.t1 values ('batman',2); 974 | Query OK, 1 row affected (0.00 sec) 975 | 976 | MariaDB [(none)]> commit; 977 | Query OK, 0 rows affected (0.00 sec) 978 | ``` 979 | 980 | T1事务 981 | 982 | ```shell 983 | MariaDB [(none)]> commit; 984 | Query OK, 0 rows affected (0.00 sec) 985 | ``` 986 | 987 | ### 实践4:next-key locking是如何解决幻读问题的 988 | 989 | ![lock19](pic/lock19.png) 990 | 991 | 首先什么是幻读呢? 992 | 993 | 举个例子,两个男孩同时在追求一个女生的故事 994 | 995 | A问:你有男朋友吗?女孩对他说没有。A追求女孩的事件还没有提交,就是继续追求哈。 996 | 997 | 就在A追求的同时,B也在追求,并且直接让女孩做他的女朋友,女孩答应了,B的追求事件结束。 998 | 999 | A又问:你有男朋友吗? 女孩对他说我已经有男朋友了! 呜呜呜 !刚才你还没有的,怎么现在就有了呢? 1000 | 1001 | 女孩说,你也没说过你追我的时候不让别人追我啊!... ... A哭着走了。 1002 | 1003 | 1004 | **幻读 Phantom Problem 是指在同一事务下,连续执行两次相同的sql语句可能导致不同的结果,第二次的sql语句可能会返回之前不存在的行。** 1005 | 1006 | 在刚才我举的例子里,A虽然问了女孩有没有男朋友,但是没有告诉女孩,在他追求时,不可以接受别人的追求,所以悲催的结局。 1007 | 1008 | 那么A怎么才能在他追求事件结束前让女孩不答应别人的追求呢? 1009 | 1010 | innodb中的RR隔离级别是通过next-key locking是如何解决幻读问题的,就是锁住一个范围。 1011 | 1012 | 那么如果你是A你怎么做呢?你肯定要跟女孩说,只要我开始追求你,问了你有没有男朋友,在我结束追求你之前,你不可以答应别人的追求!我要把你脑子里记录男朋友的区域全部锁起来,啊哈啊! 1013 | 1014 | 下面我们来做一个测试,分别在RR和RC隔离级别中来实现: 1015 | 1016 | 测试使用表db1.t1 (a int primary key) ,记录有1,3,5 1017 | 1018 | |T1 RC|T2 RR| 1019 | |:--|:--| 1020 | |begin;|begin;| 1021 | |set session transaction isolation level READ COMMITTED;|| 1022 | |select * from db1.t1 where a>3 for update;|| 1023 | |查询结果为5|| 1024 | ||insert into db1.t1 values (4);| 1025 | ||commit;| 1026 | |select * from db1.t1 where a>3;|| 1027 | |查询结果为4 5|| 1028 | 1029 | ```shell 1030 | MariaDB [db1]> create table t1 (a int primary key); 1031 | Query OK, 0 rows affected (0.22 sec) 1032 | 1033 | MariaDB [db1]> insert into t1 values (1),(3),(5); 1034 | Query OK, 3 rows affected (0.02 sec) 1035 | Records: 3 Duplicates: 0 Warnings: 0 1036 | 1037 | #事务T1 1038 | MariaDB [db1]> begin; 1039 | Query OK, 0 rows affected (0.00 sec) 1040 | 1041 | MariaDB [db1]> set session transaction isolation level read co 1042 | Query OK, 0 rows affected (0.01 sec) 1043 | 1044 | MariaDB [db1]> select * from db1.t1 where a>3 for update; 1045 | +---+ 1046 | | a | 1047 | +---+ 1048 | | 5 | 1049 | +---+ 1050 | 1 row in set (0.01 sec) 1051 | 1052 | #事务T2 1053 | MariaDB [db1]> begin; 1054 | Query OK, 0 rows affected (0.00 sec) 1055 | 1056 | MariaDB [db1]> insert into db1.t1 values (4); 1057 | Query OK, 1 row affected (0.00 sec) 1058 | 1059 | MariaDB [db1]> commit; 1060 | Query OK, 0 rows affected (0.03 sec) 1061 | 1062 | #事务T1 1063 | MariaDB [db1]> select * from db1.t1 where a>3 for update; 1064 | +---+ 1065 | | a | 1066 | +---+ 1067 | | 4 | 1068 | | 5 | 1069 | +---+ 1070 | 2 rows in set (0.00 sec) 1071 | ``` 1072 | 1073 | 1074 | 将会话中的隔离界别改为RR,并删除a=4记录。 1075 | 1076 | ```shell 1077 | MariaDB [db1]> set session transaction isolation level repeatable read; 1078 | Query OK, 0 rows affected (0.00 sec) 1079 | 1080 | MariaDB [db1]> delete from db1.t1 where a=4; 1081 | Query OK, 1 row affected (0.00 sec) 1082 | ``` 1083 | 1084 | |T1 RR|T2 RR| 1085 | |:--|:--| 1086 | |begin;|begin;| 1087 | |select * from db1.t1 where a>3 for update;|| 1088 | |查询结果为5|| 1089 | ||insert into db1.t1 values (4);| 1090 | ||ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction| 1091 | ||commit;| 1092 | |select * from db1.t1 where a>3;|| 1093 | |查询结果为5|| 1094 | 1095 | ```shell 1096 | #事务T1 1097 | MariaDB [(none)]> begin; 1098 | Query OK, 0 rows affected (0.00 sec) 1099 | 1100 | MariaDB [(none)]> select * from db1.t1 where a>3 for update; 1101 | +---+ 1102 | | a | 1103 | +---+ 1104 | | 5 | 1105 | +---+ 1106 | 1 row in set (0.02 sec) 1107 | 1108 | #事务T2 1109 | MariaDB [(none)]> begin; 1110 | Query OK, 0 rows affected (0.00 sec) 1111 | 1112 | MariaDB [(none)]> insert into db1.t1 values (4); 1113 | ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction 1114 | MariaDB [(none)]> commit; 1115 | Query OK, 0 rows affected (0.00 sec) 1116 | 1117 | #事务T1 1118 | MariaDB [(none)]> select * from db1.t1 where a>3 for update; 1119 | +---+ 1120 | | a | 1121 | +---+ 1122 | | 5 | 1123 | +---+ 1124 | 1 row in set (0.02 sec) 1125 | 1126 | ``` 1127 | 1128 | -------------------------------------------------------------------------------- /pdf/lock.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pdf/lock.odg -------------------------------------------------------------------------------- /pdf/lock.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pdf/lock.pdf -------------------------------------------------------------------------------- /pdf/mysql_innodb.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pdf/mysql_innodb.odg -------------------------------------------------------------------------------- /pdf/mysql_innodb.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pdf/mysql_innodb.pdf -------------------------------------------------------------------------------- /pic/README.md: -------------------------------------------------------------------------------- 1 | 图片 2 | -------------------------------------------------------------------------------- /pic/lock01.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock01.bmp -------------------------------------------------------------------------------- /pic/lock01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock01.png -------------------------------------------------------------------------------- /pic/lock02.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock02.bmp -------------------------------------------------------------------------------- /pic/lock02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock02.png -------------------------------------------------------------------------------- /pic/lock03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock03.png -------------------------------------------------------------------------------- /pic/lock04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock04.png -------------------------------------------------------------------------------- /pic/lock05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock05.png -------------------------------------------------------------------------------- /pic/lock06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock06.png -------------------------------------------------------------------------------- /pic/lock07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock07.png -------------------------------------------------------------------------------- /pic/lock08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock08.png -------------------------------------------------------------------------------- /pic/lock09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock09.png -------------------------------------------------------------------------------- /pic/lock10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock10.png -------------------------------------------------------------------------------- /pic/lock11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock11.png -------------------------------------------------------------------------------- /pic/lock13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock13.png -------------------------------------------------------------------------------- /pic/lock14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock14.png -------------------------------------------------------------------------------- /pic/lock15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock15.png -------------------------------------------------------------------------------- /pic/lock16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock16.png -------------------------------------------------------------------------------- /pic/lock17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock17.png -------------------------------------------------------------------------------- /pic/lock18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock18.png -------------------------------------------------------------------------------- /pic/lock19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoobooWei/inside-innodb/e897710e7567948d06f6dfd565ce6e1fc4b1f09e/pic/lock19.png --------------------------------------------------------------------------------