阅读打卡mysql部分汇总(2)| 青训营笔记

61 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天

MySQL 一条语句的生命过程

  • 诞生

    • 生成方式

      • 开发者编写
      • ORM框架自动生成,如Java中的MyBatis、Hibernate框架等
    • 生成时机——与用户请求有关
  • 执行前

    • 数据库连接池(如如Java中的C3P0、Druid、DBCP)——将数据库连接这种较为珍贵的资源,利用池化技术对这种资源进行维护

      • MySQL连接池维护的是工作线程
      • 客户端连接池则维护的是网络连接
  • 执行

    • 查询select

      • 步骤

        • ①先将SQL发送给SQL接口,SQL接口会对SQL语句进行哈希处理。
        • ②SQL接口在缓存中根据哈希值检索数据,如果缓存中有则直接返回数据。
        • ③缓存中未命中时会将SQL交给解析器,解析器会判断SQL语句是否正确:

          • 错误:抛出1064错误码及相关的语法错误信息。
          • 正确:将SQL语句交给优化器处理,进入第④步。
        • ④优化器根据SQL制定出不同的执行方案,并择选出最优的执行计划。
        • ⑤工作线程根据执行计划,调用存储引擎所提供的API获取数据。
        • ⑥存储引擎根据API调用方的操作,去磁盘中检索数据(索引、表数据....)。
        • ⑦发生磁盘IO后,对于磁盘中符合要求的数据逐条返回给SQL接口。
        • ⑧SQL接口会对所有的结果集进行处理(剔除列、合并数据....)并返回。
      • 优化器 一些优化准则

        • ❶多条件查询时,重排条件先后顺序,将效率更好的字段条件放在前面。
        • ❷当表中存在多个索引时,选择效率最高的索引作为本次查询的目标索引。
        • ❸使用分页Limit关键字时,查询到对应的数据条数后终止扫表。
        • ❹多表join联查时,对查询表的顺序重新定义,同样以效率为准。
        • ❺对于SQL中使用函数时,如count()、max()、min()...,根据情况选择最优方案。

          • max()函数:走B+树最右侧的节点查询(大的在右,小的在左)。
          • min()函数:走B+树最左侧的节点查询。
          • count()函数:如果是MyISAM引擎,直接获取引擎统计的总行数。
          • ......
        • ❻对于group by分组排序,会先查询所有数据后再统一排序,而不是一开始就排序。
        • ❼......
    • 写入

      • 步骤

        • ①先将SQL发送给SQL接口,SQL接口会对SQL语句进行哈希处理。
        • ②在缓存中根据哈希值检索数据,如果缓存中有则将对应表的所有缓存全部删除。//确保缓存的强一致性
        • ③经过缓存后会将SQL交给解析器,解析器会判断SQL语句是否正确:

          • 错误:抛出1064错误码及相关的语法错误信息。
          • 正确:将SQL语句交给优化器处理,进入第④步。
        • ④优化器根据SQL制定出不同的执行方案,并择选出最优的执行计划。
        • ⑤在执行开始之前,先记录一下undo-log日志和redo-log(prepare状态)日志。
        • ⑥在缓冲区中查找是否存在当前要操作的行记录或表数据(内存中):

          • 存在:

            • ⑦直接对缓冲区中的数据进行写操作。
            • ⑧然后利用Checkpoint机制刷写到磁盘。
          • 不存在:

            • ⑦根据执行计划,调用存储引擎的API。
            • ⑧发生磁盘IO,对磁盘中的数据做写操作。
        • ⑨写操作完成后,记录bin-log日志,同时将redo-log日志中的记录改为commit状态。
        • ⑩将SQL执行耗时及操作成功的结果返回给SQL接口,再由SQL接口返回给客户端。
      • 不同之处

        • 唯一性判断 主要是针对插入、修改语句来说的,因为如果表中的某个字段建立了唯一约束或唯一索引后,当插入/修改一条数据时,就会先检测一下目前插入/修改的值,是否与表中的唯一字段存在冲突,如果表中已经存在相同的值,则会直接抛出异常,反之会继续执行。
        • 缓冲区

          • 在真正调用存储引擎的API操作磁盘之前,首先会在「缓冲区」中查找有没有要操作的目标数据/目标表,如果存在则直接对缓冲区中的数据进行操作,然后MySQL会在后台以一种名为Checkpoint的机制,将缓冲区中更新的数据刷回到磁盘。只有当缓冲区没有找到目标数据时,才会去真正调用存储引擎的API,然后发生磁盘IO,去对应磁盘中的表数据进行修改。
          • note:虽然缓冲区中有数据时会先操作缓冲区,然后再通过Checkpoint机制刷写磁盘,但这两个过程不是连续的!也就是说,当线程对缓冲区中的数据操作完成后,会直接往下走,数据落盘的工作则会交给后台线程。
        • 日志

          • 读 只有慢查询
          • 所有的写SQL在执行之前都会生成对应的撤销SQL,撤销SQL也就是相反的操作,比如现在执行的是insert语句,那这里就生成对应的delete语句....,然后记录在undo-log撤销/回滚日志中。但除此之外,还会记录redo-log日志。

            • redo-log日志是InnoDB引擎专属的,主要是为了保证事务的原子性和持久性,这里会将写SQL的事务过程记录在案,如果服务器或者MySQL宕机,重启时就可以通过redo_log日志恢复更新的数据。在「写SQL」正式执行之前,就会先记录一条prepare状态的日志,表示当前「写SQL」准备执行,然后当执行完成并且事务提交后,这条日志记录的状态才会更改为commit状态。
            • 同时还会记录bin-log日志,这个日志和redo-log日志很像,都是记录对数据库发生更改的SQL,只不过redo-log是InnoDB引擎专属的,而bin-log日志则是MySQL自带的日志。
          • 先写到缓冲区中,然后再异步刷写到磁盘

            • 刷盘策略

              • redo-log日志的刷盘策略由innodb_flush_log_at_trx_commit参数控制

                • 0:间隔一段时间,然后再刷写一次日志到磁盘(性能最佳)。
                • 1:每次提交事务时,都刷写一次日志到磁盘(性能最差,最安全,默认策略)。
                • 2:有事务提交的情况下,每间隔一秒时间刷写一次日志到磁盘。
              • bin-log日志的刷盘策略则可以通过sync_binlog参数控制:

                • 0:同上述innodb_flush_log_at_trx_commit参数的2。
                • 1:同上述innodb_flush_log_at_trx_commit参数的1,每次提交事务都会刷盘,默认策略。
  • 返回(SQL接口)

    • 读类型

      • 逐条返回
    • 写类型

      • 仅会返回执行状态、受影响的行数以及执行耗时
    • 执行结果是如何返回给客户端的

      • 由于执行当前SQL的工作线程,本身也维护着一个数据库连接,这个数据库连接实际上也维持着客户端的网络连接
      • 当结果集处理好了之后,直接通过Host中记录的地址,将结果集封装成TCP数据报,然后返回即可
      • 数据返回给客户端之后,除非客户端主动输入exit等退出连接的命令,否则连接不会立马断开.如果要断开客户端连接时,又会经过TCP四次挥手的过程。
      • 不过就算与客户端断开了连接,MySQL中创建的线程并不会销毁,而是会放入到MySQL的连接池中,等待其他客户端复用当前连接。一般情况下,一条线程在八小时内未被复用,才会触发MySQL的销毁工作。