mysql架构
客户端<+++连接器+++>server+存储引擎
1条MySQL语句怎么执行(结构)
结论 连接器、分析器 优化器 执行器
连接器:同一个连接的权限在连接器连接时确定,中途修改权限必须重新连接才能生效
分析器:词法分析(字符串识别成表名、列名等)+语法分析
优化器:结果就是生成一个执行计划:决定使用索引,决定join连接顺序
执行器:检查权限,打开表\
1条MySQL语句怎么更新(日志系统)
结论:上一条+写日志[先写日志,再写磁盘]
redolog:InnoDB提供物理日志(重做日志),记录数据做了什么修改,循环写。需要更新写redo日志,更新内存,不忙时候写回磁盘,异常重启后redolog还在,提交记录不丢失。
binlog:MySQL server提供逻辑日志(归档日志),记录操作逻辑,追加写。
为了数据库一致性,采用两阶段提交:写入redolog到prepare状态,写入binlog后到commit状态\
事务隔离
ACID(I)
隔离级别:
- 读未提交:事务未提交,其更改可被其他事务看到
- 读提交:事务提交,其更改才会被其他事务看到
- 可重复读:一个事务执行过程看到的数据,与事务启动时看到的数据一致
- 串行化:加锁,读写冲突必须等待。
不要用长事务
占用存储空间,占用锁资源
索引
上
常见模型
| 类别 | 特性 |
|---|---|
| 哈希 | KEY-VALUE,适用于等值查询 |
| 有序数组 | 等值和范围查询,适用于静态存储引擎 |
| N叉树 | B+树读写性能,适配磁盘访问模式 |
InnoDB索引
- 主键索引叶子节点是整行数据,非主键索引叶子节点是主键值,非主键索引查询需要多扫描一棵索引树。
- B+树插入删除数据需要页分裂、合并等操作,因此递增索引插入都是追加操作,不会导致叶子节点分裂
下(联合索引技巧)
- 索引覆盖:查询条件是普通索引,查询结果是联合索引的字段,不用回表直接返回
- 最左前缀:联合索引的最左N字段,字符串索引最左M字段
- 联合索引:根据联合索引的顺序,以最左原则检查
- 索引下推:like 'hello%’and age >10 检索,MySQL5.6版本之前,会对匹配的数据进行回表查询。5.6版本后,会先过滤掉age<10的数据,再进行回表查询,减少回表率,提升检索速度。
MYSQL锁
全局锁 备份
对数据库实例加锁,flush tables with read lock,全库处于只读状态
场景:不支持事务的引擎,全库逻辑备份
支持事务后,用可重复读隔离级别开启事务single transaction备份即可,主库更新等操作不受影响\
表级锁
表锁
语法: lock tables read/write
元数据锁MDL
自动添加,不需
要显示执行
作用:保证读写正确性
增删改查用读锁,表结构变更用写锁
读锁之间不互斥,读写锁、写锁之间互斥
问题:事务的MDL锁语句开始时执行,语句结束不被释放,等事务结束才释放,可能导致后续事务一直阻塞等待。
解决方案:(写锁)数据变更过程,设定等待时间,拿不到锁先放弃
行锁
两阶段锁
行锁需要等到事务结束才释放。因此把最可能造成冲突,影响并发度的锁往后放。
死锁&&死锁检测
处理死锁策略
- 等待到超时
- 死锁检测,发现死锁,回滚链条中的某一个事务
减少锁冲突
将一行改成逻辑上多行,减少锁冲突。
本质:控制相同资源的并发事务量
再谈事务隔离
再谈查询和更新两个操作的事务隔离
快照
InnoDB给每个事务分配一个id:row trx_id,一个数组记录启动时全部活跃的事务
每个数据都有多个版本,用row trx_id来区分,undolog就是通过版本间的变化来存储记录的\
更新
事务更新先读后写,读就是读当前值,当前读。
注意: 若select加锁,也是当前读
可重复读的核心就是一致性读(consistent read);而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。\
- 对于可重复读,查询只承认在事务启动前就已经提交完成的数据;
- 对于读提交,查询只承认在语句启动前就已经提交完成的数据;
- 而当前读,总是读取已经提交完成的最新版本。