MySQL
大负载
InnoDB存储
文件:
数据库路径:/var/lib/mysql/db_name
db.opt,用来存储当前数据库的默认字符集和字符校验规则
每个表分别有table_name.frm和table_name.idb两个文件,分别存储表结构和表数据
Table space:
- leaf node segment
- non-leaf node segment
- rollback segment
Segment
- Extent 1M
- Page
Extent:
- Page 16K,1M / 16K = 64个Page
Page:
- Record,单向链表
- extra info,定长
- Page directory,槽,按主键顺序存放,一个槽可以放多个主键,二分,定长
每个记录大小不同
一条记录的存储
变长字段长度+NULL值列表+头部信息+row_id(可选)+trx_id+rollback_ptr+真实数据
一行记录最长65536字节,而一页是16K = 16384字节
InnoDB索引
主键
InnoDB的存储是按照主键来存储的,每个表必须有一个主键,不指定的话会自动生成隐藏主键。
B+树
节点单位是页
通常删除都是做逻辑删除而不是物理删除,因为真删除的话会导致页和页之间的合并,影响效率。
哈希索引
当一个页被加载到内存后,可以通过哈希,让主键key作为key,对应的内存地址作为value构建哈希索引。
辅助索引
尽量减少辅助索引的个数,因为插入会不可避免的带来分裂问题
高并发
InnoDB事务
隔离级别
- Read Uncommited,不做隔离,acid全都没有
- Read Commited,隔离性不好,一个事务期间读相同的数据可能值不同(因为别的事务修改,自己修改不算),好的隔离性是说如果一个事务期间自己没有对一个数据做修改那么每次读到的数据都应该是一致的,排它锁
- Repeatable Read,会有幻读情况,排它锁+共享锁
- 串行化,表锁
MVCC(多版本并发控制(控制的是读),一致性锁定读)
一句话总结:是MySQL基于回滚机制(undo log(mvcc的底层原理))为并发场景下的读操作做的优化
记录的隐藏字段
- DB_TRX_ID:事务id,通常是一个数
- DB_ROLL_PTR
快照
每个事务的快照包含:
- trx_ids:当前事务开始前所有活跃的事务id集合
- low_limit_id:下一个事务id
- up_limit_id:trx_ids集合当中的最小值
Repeatable Read的快照:事务开始时产生
Read Commited:每次select的时候生成Read View
如果读到记录中的事务id比up_limit_id还小,说明在我事务开始前这个事务已经提交了,记录可看
同理,如果读到记录中的事务id大于等于low_limit_id,说明你这个事务虽然提交了,但是在你开始之前我就开始了,所以这条记录不能被我看见
如果事务id大于等于up_limit_id小于low_limit_id,那么要分情况讨论,如果事务id在trx_ids集合中,说明我开始之前已经开始但没有提交修改的事务在我还没提交之前提交了修改,所以我不能看(因为我开始的时候这条数据没有提交);否则说明我事务开始的时候修改这个数据的事务已经提交了,所以可以看
MVCC只能解决读的问题,写的问题无法解决
还有一个问题,比如库存的减少场景
锁
三种方式:锁记录,锁范围,next-key lock
日志
为什么mysql索引不用跳表?
- 不适合磁盘存储
- 联合索引
Redo log
redo log记录的是内存页的修改逻辑
binlog存的是执行语句的日志
1. 批量插入时的瓶颈:MySQL id自增长
在5.1.22版本之前,MySQL对于所有的插入采用的都是auto increase locking,轻量,锁定的是innodb里面的自增长计数器。释放的时机是插入完成后事务提交之前,不需要等到commit。
5.1.22之后,将单一的模式调整成3个模式:
- 第一种不变;
- 第二种:(默认)如果一直插入数据数量的情况下,使用互斥量的方式完成id自增长,比轻量级锁更轻量。如果不知道数量的情况下就还是用第一种
- 第三种:无论如何都用互斥量,会导致id不连续(会威胁到主从数据同步的问题)
2. 并发插入时的瓶颈:commit
SQL语句执行完commit前,redo log是缓存形式的,在内存buffer里面
执行commit之后,才将内存中的redo log内容刷到磁盘
三种策略:
- 每commit一次刷新一次磁盘(最好的策略)(数据分批插入)
- 每1秒刷新一次(执行SQL的线程刷新)
- 不刷新
binlog
主从同步