MySQL学习笔记

一条MYSQL的执行流程

MYSQL组件

  • 连接器

    • 用户名密码验证
    • 查询用户权限,分配对应权限
    • show processlist查看现在的连接
    • 推荐长连接,长时间没有反应会自动断开,默认8小时(wait_timeout)
  • 分析器

    • 词法分析

      根据关键词分段识别表名,列名,查询条件等

    • 语法分析

      根据语法规则判断sql是否符合mysql语法(You have an error in your SQL syntax)

  • 优化器

    对sql的执行顺序和查询逻辑进行优化,比如:

    • 多个索引选择用哪个索引
    • sql多表关联的时候,决定表的连接顺序(A left join B left join C,具体查询哪张表由sql优化器决定,不存在什么小表在前大表在后会优化查询速度的事情)
不同优化规则对SQL执行效率影响很大

*   CBO 基于成本优化(常用)
    
*   RBO 基于规则优化
    
  • 执行器

    数据库服务器Server端通过执行器,连接存储引擎,由存储引擎进行数据查询。

  • binlog

    数据库server层的日志,记录所有的逻辑,采用追加写的方式。mysql集群主从复制主要使用binlog;支持定期备份。

    恢复数据的过程:

    • 找到最近一次全量备份数据
    • 从备份的时间点开始,将备份的binlog取出来,重放到要恢复的那个时刻
与redo log区别在于:

*   binlog是服务端日志,redo log是InnoDB存储引擎独有的
    
*   binlog是逻辑日志,记录的是这个语句的原始逻辑;redo log是物理日志,记录的是数据页做了什么修改
    
*   redo log是循环使用,空间会用完;binlog是可以追加写的,不会覆盖之前的日志信息
    
  • redo log

    • InnoDB存储引擎特有的日志文件,每有数据更新时,InnoDB引擎先将记录写到redo log并更新到内存
    • redo log固定大小,是循环写的过程,空间用完后覆盖之前的日志
    • crash-safe:保证数据库异常重启,之前的记录也不会丢失
  • undo log

    • InnoDB存储引擎中用来实现事务的原子性
    • 修改任何数据,将指令备份在Undo log,然后进行数据的修改。如果修改失败则执行RollBack,系统利用Undo log中的备份将数据恢复到修改之前
    • 逻辑日志。比如:当更新语句是一条delete语句,undo log对应记录一条insert记录

服务端执行流程

MySQL学习笔记_第1张图片

查询语句的执行流程

  • 权限校验(如果命中索引) → 查询缓存(MYSQL8.0以前) → 分析器 → 优化器 → 执行器 → 存储引擎

更新语句执行流程

  • 分析器 → 权限校验 → 优化器 → 执行器 → 存储引擎 → redo log(prepare状态) → binlog → redo log(commit状态)
  • redo log两阶段提交,保证数据一致性

    • 当判断redo log是完整的,直接提交事务
    • 当redo log是prepare状态而不是commit状态,则判断binlog是否是完整的,如果是则更新undo log为commit状态并提交事务,如果不是则回滚事务

MYSQL锁

事务四大特性ACID

原子性、一致性、隔离性、持久性。最终目标是一致性。

并发事务问题

  • 脏读:事务A修改数据1,事务B读数据1,事务A修改还未提交到数据库,事务B读取到之前的“脏数据”
  • 丢失修改:事务A和事务B同时修改数据1,事务A修改数据后,事务B再对数据进行修改,最后事务A的修改结果丢失。例:事务A读取数据T=20,修改为T=T-1,事务B同样修改数据为T=T-1,结果T=19
  • 不可重复读:事务A多次查询同一数据,在事务A没有结束前,数据B修改这个数据,事务A读取这个数据的值前后不一致
  • 幻读:和不可重复读类似。区别是不可重复读是读取的原数据被修改,幻读是读取的数据多出来几行,就跟发生幻觉一样。

事务隔离级别

  • 读未提交
  • 读已提交(公司目前配置)
  • 可重复读(InnoDB 存储引擎默认支持隔离级别,InnoDB在这个隔离级别下使用的是Next-Key-Lock锁算法,因此可以避免幻读,所以InnoDB的可重复读隔离级别已经达到了SQL标准的序列化级别)
  • 序列化

MylSAM表锁

  • 共享读锁和独占写锁

InnoDB锁机制

  • InnoDB锁加在索引上,命中到索引,走行锁;没命中索引走表锁
  • InnoDB的锁算法有三种:

    • Record lock:单行记录上的锁
    • Gap lock:间隙锁,锁定一个范围,不包括记录本身
    • Next-key lock:相当于Record lock+ Gap lock 锁定一个范围,包括记录本身(InnoDB引擎在可重复读隔离级别解决幻读问题所使用的锁
  • InnoDB对行查询是用的是Next-key lock
  • Next-key lock:为了解决幻读问题
  • 当查询是命中唯一索引,InnoDB将Next-key lock降为Record lock
  • 间隔锁(GAP):当使用范围条件检索数据,而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合范围条件的数据加锁,即使这些数据不存在。比如:数据库有1-100的数据,请求>99的数据,InnoDB会给100这条数据和大于100的这些"间隙"加锁(即使大于100这些记录并不存在)。

    • 防止幻读:InnoDB通过 事务级别的隔离(可重复读) + 间隙锁可以避免幻读
    • MySQL的恢复机制要求:在一个事务未提交前,其他并发事务不能插入满足其锁定条件的任何记录,也就是不允许出现幻读

MySQL如何减少死锁

  • 固定的顺序访问表和行。比如对两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;将两个事务的sql顺序调整为一致,也能避免死锁
  • 将大事务拆小。
  • 降低隔离级别。从Reaptable read(InnoDB默认)降到read commit,防止间隙锁(GAP锁)造成的死锁
  • 为表增加合理的索引。因为如果不走索引会触发表锁,死锁概率增大

MylSAM和InnoDB区别

  • 是否支持行级锁:MylSAM只有表锁,InnoDB有表锁和行锁
  • 是否支持事务和崩溃后的安全恢复:MylSAM强调性能,每次查询具有原子性,比InnoDB执行效率高,但不支持事务;但是InnoDB 提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表
  • 是否支持外键:MylSAM不支持,InnoDB支持
  • 是否支持MVCC:InnoDB支持

    InnoDB基于行锁实现MVCC:https://baijiahao.baidu.com/s?id=1629409989970483292&wfr=spider&for=pc

MYSQL索引

为什么使用索引?

  • 减少服务器扫描的数据量
  • 帮助服务器避免排序和临时表
  • 将随机IO变成顺序IO,降低IO消耗提升效率
  • 使用唯一性索引可以保证某列数据的唯一性

为什么不给每列加索引?

  • 增删改数据时,索引也要动态维护
  • 索引会占用物理空间,耗费资源

索引提高查询速度原理

  • 查询速度提升:使用索引可以让无序的数据变得有序(相对的),底层结构是B+树,可以通过二分法快速找到数据所在数据页,再快速遍历数据页里面的数据获得对应记录即可。如果没有使用索引,就需要遍历双向链表找到数据所在数据页,但使用了索引就通过目录就可以定位到对应页了。
  • 增删改速度降低:B+树是一种平衡树,特点是空树或者左右子树高度绝对值不超过1,且左右子树都是平衡二叉树,检索的时间复杂度就是O(logn),因为普通树结构在极端情况下会左右子树过长导致退化成链表,查询效率不复存在,建立索引就是建立了一颗B+树,所以增删改是在破坏B+树的结构,维护B+树结构需要额外的开销(左旋右旋),导致索引会降低增删改的速度

索引创建

InnoDB通过B+树结构对主键创建索引,然后叶子节点存储数据记录,如果没有主键,则使用唯一键,如果没有唯一键则生成一个6位的row_id作为主键

索引数据结构

  • 哈希表
  • B+树

MylSAM和InnoDB实现索引区别

  • MylSAM使用非聚簇索引

    B+树叶子节点的data域存放的是数据记录的地址。当命中索引查询到key,取出data域中的数据记录地址,再读取地址所在数据。

  • InnoDB使用聚簇索引
  • B+树叶子节点的data域直接存放的是数据本身,而这个树的key是数据表的主键,因此,InnoDB的表数据文件本身就是主索引。而其他索引都作为辅助索引,辅助索引的data域记录的是主键的值而不是地址(这也是和MylSAM不同的地方),根据辅助索引查询时,先通过辅助索引查询出主键值,再走主索引查询到对应的数据。因此不建议使用过长字段作为主键。

索引分类

  • 主键索引
  • 唯一索引
  • 普通索引
  • 全文索引
  • 组合索引

相关名词

  • 回表

    当使用非主键索引进行查询时,在辅助索引叶子节点查询到主键值,再走主索引查询到数据记录,才完成整个查询。

  • 覆盖索引

    使用辅助索引查询时,当需要查询的字段没有在当前索引下时(查询的字段中有没有命中索引或者没有创建索引的字段),需要回表查询,查询效率降低,但如果需要查询的字段就在当前辅助索引的Key值就不需要回表,提升效率。例:select age,name from student where age = 11 and name like '王%' 如果有age,name的联合索引,索引命中并且查询的就是索引字段,就不需要回表,直接辅助索引走完就可以返回,叫做覆盖索引。

  • 最左匹配

    • 使用联合索引时,会首先匹配最左侧的索引字段,如果两个查询条件,顺序不同,优化器也会优化成匹配联合索引的顺序,也能命中联合索引。
    • 由于最左匹配原则,在创建联合索引时,索引字段顺序需要考虑把去重后字段个数值多的放在前面。ORDER BY也遵循这个原则(多个字段排序时,也是把去重后字段值个数多的放在排序前面)
  • 索引下推

    联合索引时,根据最左匹配如果只匹配到查询条件的前几项,那存储引擎就会根据这几项去查询,再逐个回表,最后在mysql服务器再对其他条件进行匹配。

    使用索引下推,会把所有索引的查询条件都发给(下推)存储引擎,直接筛选后,再将筛选后的数据进行回表,这样就减少了回表次数,增加查询效率。

    e.g.

    • 组合索引:name,age
    • 查询sql:select *from person where name like "张%" and age = 10
    • 如果不使用索引下推,根据最左匹配,只会命中name的索引,age的查询条件不会被命中,存储引擎就会查询出更多的数据就会变多,回表次数变多,查询变慢
    • 使用索引下推,sql服务器直接把条件推给存储引擎(下推),存储引擎查询数据时直接筛选符合name和age条件的数据进行回表,减少回表次数,提高查询效率

索引匹配方式

  • 全值匹配
  • 最左匹配:匹配前几列命中的索引
  • 匹配列前缀:匹配某列的开头一部分(explain select * from staffs where name like 'J%')
  • 匹配范围值:查找某一范围的数据(先匹配到范围查询,后面的索引不会命中)
  • 精确匹配某一列并范围匹配另外一列:explain select * from staffs where name = 'July' and age > 25;

排序优化

当不能使用索引扫描进行排序时,mysql需要自己排序,如果数据量小则在内存进行排序,如果数据量大则需要使用磁盘(mysql称为filesort)。如果需要排序的数据量小于排序缓冲区(show variables like '%sort_buffer_size%'),则使用内存进行单次排序即可;

如果内存不足,mysql会先将数据分块,对每个独立块进行快速排序,放在磁盘上,然后再将排好序的数据合并,返回排序结果。

  • 两次排序

    • 第一次先将需要排序的字段读取出来,对这些字段进行排序;排序完毕后再根据排序的结果读取数据
    • 坏处:效率比较低,因为在用排好序的字段在读取所有记录的时候,更多的是随机IO,性能低
    • 好处:排序时需要缓存的数据少,让排序缓冲区能够容纳尽可能多的字段进行排序
  • 单次排序

    • 先查询所需要的所有列,再根据给定列进行排序,最后返回排序结果
    • 好处:只需要一次顺序IO读取所有的数据,而无须任何的随机IO
    • 需要查询的列过多时会占用大量存储空间,无法存储大量数据
  • 排序选择

    当需要排序的列的总大小超过max_length_for_sort_data定义的字节,mysql会选择双次排序,反之使用单次排序,当然,用户可以设置此参数的值来选择排序的方式

MySQL集群

MYSQL主从复制

  • 为什么需要主从复制
  • 使用主从复制,让主库负责写,从库负责读,即使主库出现锁表的场景,通过读从库也可以保证读操作不受影响
  • 数据的热备份
  • 架构扩展,分散单机磁盘I/O的访问频率,提升单个机器I/O性能
  • 主从复制原理(binlog
  1. master每次对数据的改变记录到binlog二进制日志中,授予slave远程连接的权限(主从都需要开启binlog功能保证安全)
  2. 从库每隔一段时间,会对master的binlog日志进行探测,如果发生了改变,则启动一个I/O thread请求读取主库的binlog日志
  3. 这时候主库也会为每一个I/O线程启动一个dump线程,用于向其发送二进制事件,从节点将数据保存至中继日志(relay log)中
  4. 从节点会启动SQL thread从中继日志中读取二进制日志,在本地重放,使主从库数据一致。之后I/O线程和SQL线程将进入休眠等待下一次唤醒

注意:

  • 主从模式至少需要两个mysql服务,可以分布在不同服务器上,也可以在一台服务器上启动多个服务
  • Mysql复制确保master和slave服务器的Mysql版本相同或slave节点Mysql版本高于master
  • master和slave节点需要时间同步

分库分表

https://crossoverjie.top/2019/07/24/framework-design/sharding-db-03/(写得好)

你可能感兴趣的:(mysqljava)