MySQL 区别于其他数据库的最为重要的生点就是, 插件式的表存储引擎. 而在众多存储引擎中, InnoDB 最为常用的存储引擎.
InnoDB 存储引擎事务处下 (OLTP) 的应用. 其特点是行锁设计, 支持外键, 并支持非锁定读, 即默认读操作不会产生锁.
InnoDB 通过多版本并发控制 (MVCC) 来获取高并发性, 并实现 sql 中四个隔离级别, 默认为 REPEATABLE 级别.
同时一种称为 next-key-locking 的策略来避免幻读现象的产生. 除此之外, InnoDB 存储引擎还提供了
插入缓冲 (insert buffer) , 二次写 (double write), 自适应哈希索引 (adaptive hash index)
预读 (read ahead) 等高性能和高可用的功能.
缓冲池: InnoDB 存储引擎是其于磁盘存储的, 并将其中的记录按照页折方式进行管理.
但由于 CPU 速度和磁盘速之间的鸿沟, 其于磁盘的数据库系统通常使用缓冲池记录来提高数据库的整体能力.
数据库中进行数据读取, 首先从磁盘中读取的页放到缓存中, 再读取相同的页中, 首先
判断该页在缓存中被命中, 直接读取该页. 否则直接从磁盘中读取.
对于数据中页的修改操作, 首先修改在缓冲区的页, 然后再以一定的频率刷新到磁盘上, 不是在更新时时触发,
而是通过一种称为 CheckPoint 的机制刷新回磁盘.
所以缓冲池大小直接影响数据库的整体性能, 通过配置参数 innodb_buffer_pool_size 来进行设置.
数据页和索引页
InnoDB 引擎进行插入操作, 按照主键的顺序进行插入, 当一个表存在非聚簇索引且不是唯一索引时插入时,
数据页的存放还是按主键进行顺序存放, 但对于非聚簇索引, 的叶子节点插入数据已经不是按顺序进行存放的了.
这时就需要离散的访问非聚簇索引, 由随机读取的存在导致插入的性能下除(离散数学)
Double Write(两次写)
如果说 Insert Buffer 和 InnoDB 存储引擎带来了情能上的提升
那么 Double Write 带给 InnoDB 数据页的可靠性.
- page---> copy --> double write buffer
- page---> copy --> double write buffer
- copy double write buffer ---> double write
copy double write buffer ---> 数据文件 .idb (磁盘)
Double Write 由两部分组成
一部分是 double write buffer 大小约为 2M
另一部分由物理磁盘共享表 128 页 大小也是 2M
在对缓冲池的脏页进行刷新时, 不直接写磁盘, 而是调用 memcpy 函数将脏页复制到内存中的该区域,
通过 double write buffer 再分两次, 每次 1MB 写入 共享表空间, 然后调用 fsync 函数来同步磁盘.
**** 如果写入磁盘过程崩溃, innodb 可以在共享表空间找到该页的一个副本, 将其复制到空间文件中.
重做日志:
当缓冲池中的页的版本比磁盘还要新时, 数据需要将新版本的页刷新到磁盘中.
但如果每个页发送变化, 开销大太, 于是采用了, Write aHead Log 策略, 即当事务提交时, 先写重做日志
如果出现宕机导致数据丢失, 就通过重做日志进行数据恢复.
Buffer Pool ------ 先写 redo log ----- nodo log buffer
InnoDB I / O Thread
依赖 checkpoint ---- 按时或者事务提交时写入磁盘
表空间
Double --- 数据, 索引 , 等等 <------ 数据丢失时, 从 redo log file 恢复
重做日志: 不需要设置很大, 一般情况, 每一秒都会重作日志刷新到缓存中.
通过参数 innodb_log_buffer_size 控制, 一般为 8M
InnoDB 是事务的存储引擎, 通过 Force Log at Commit 机制实现事务的持久性, 即当事务提交时,
必须事务的所有日志写入到重做日志, 进行持久化.
每次重作日志后必须调用一次 fsync 操作, 将缓存文件从从文件系统缓存中真正写入磁盘.
可以通过 innodb_flush_log_at_trx_commit 来控制做日志刷新到磁盘的策略
该参数默认参数为 1 表示事务提交必须进行一次 fsync 操作,
0: 表示事务提交时, 不进行日志重做操作.
2: 提交时写入重做日志操作, 但只写入文件缓存, 不进行 fsync 操作.
自适应哈希索引 (Adaptive Hash Index)
InnoDB 根据访问频率或模式, 为热点建立 hash 索引, 自适应 hash 索引通过 B+ 树页构建起来的,
只是有一个要求这个页连续访问模式必须是一样的. 也就是查询条件 where 必须是完全一样的,
而且必是是连续的.
通过 SHOW ENGINE INNODB STATUS 可以看到当前自适应哈希索引的使用情况
查看是否被打开 show variables like '%ap%hash_index';
经常访问二级索引会被自动生成的 hash 索引里面去.(最近连续被访问三次的数据), 自适应哈希索引通过缓冲池的 B + 树构造而来,
因此建立的速度很快.
优点:
1. 无序, 没有树高
2. 降低对二级索引树的频繁访问资源
索引树高<=4 , 访问索引: 访问树, 根节点, 叶子节点
3. 自适应
缺点:
1.hash 自适应 会点用 innodb buffer pool
2. 自适应索引, 适合值查找, where idx=11 对其它类型查找, 如范围查找, 是不能使用的.
3. 极端情况, 自适应 hash 索引才比较有意义, 可以降低逻辑读.
限制:
1. 只能用于等值比较, 例如 =, <=>,in
2. 无法用于排序
3. 有可能会冲突
4.MySQL 自身管理, 无法人为干预
数据字典 --> information_schema
MVCC (Multiversion Concurrency Control) 即多版本并发控制的技术, 它使得大部分支持行锁的事务引擎, 不再单纯的使用行锁来进行
数据库的并发控制, 取而代之的是把数据库的行锁的多个版本结合起来, 只需要很小的开销, 就可以实现非锁定读, 从而大大提高
数据库系统的并发性能.
读锁: 也叫共享锁: S 锁, 若事务 T 对事务 A 加上 S 锁, 事务 T 可以读 A 也可以修改 A, 其它事务不能再对事务 A 加任何锁,
直到 T 释放 A 上的锁. 这保证了事务在 T 释放 A 上的锁之前不能再对 A 做任务的修改.
写锁: 又称排它锁, X 锁, 若事务 T 对数据对象加上 X 锁. 事务 T 可以读 A 也可以修改 A, 其它事务不能再对 A 加任何锁,
直到 T 释放 A 上的锁. 这保证了其它事务在 T 释放 A 上的锁之前不能再读取和修改 A.
表锁: 操作对象是数据表, 是系统开销最低和并发最低的锁策略. 事务 T 对整个事务加读锁, 其它事务可读不可写, 若加写锁,
其它事务增删改都不行.
行级锁: 操作对象数据表的一行, 但 Myisam 用不了, 行级锁用 MySQL 存储引擎实现而不是 MySQL 服务器. 但对行级锁开销比较大,
处理高并发较好.
MVCC 实现原理:
InnoDB MVCC 主要是 Repeatable-Read 事务隔离级别做的. 在此隔离级别下, A,B 客户端所示的数据隔离, 互相更新不可见,
了解 InnoDB 的 行结构, Read-View 的结构对于理解 innodb mvcc 重要意义:
innodb 一行, 存储额外信息如下: data_trx_id,data_roll_ptr,db_row_id,delete bit
6 字节的 data_trx_id 标记了更新这条记录 transation_id 每个一处理事务, 其值自动 + 1
7 字节的 data_roll_id 指向当前记录的 rollback segement 的 undo log 记录, 找之前的这个版本就是通过这个指针.
6 字节的 db_row_id, 当由 innodb 自动产生聚集时, 聚集索引包含 db_row_id 的值, 否则聚集索引不包含 这个值, 这个索引当中,
delete bit 位用于标识记录是否被删除, 这里的不是真正的删除数据, 而是标志出来的删除, 真正意义是 commit 的时候.
来源: http://www.jianshu.com/p/53c665a70586