mysql索引原理、主从延迟问题及如何避免

本文讲一下mysql的整体查询过程

基本的框架

客户端    - >  连接器  - > 分析器  -> 优化器  - >执行器  - > 存储引擎 

             - > 查询缓存 -

这里还有一个缓存的位置,是在连接器处,如果缓存中存在要查询的结果则直接走缓存返回

但在现实中开启缓存的几率比较低

原因:对于一个表的更新操作,这个表上的所有查询缓存都会被清空

因此除了很少更新的配置表外可以使用查询缓存来提高查询速度,一般不建议开启查询缓存

分析器:分析语法及词法,保证sql的正确性

优化器:一条sql可以通过不同的方式获取数据,优化器需要找到最优的查询方式

查找的依赖:统计信息和代码模型

例: select * from A where a = 3 and b = 4 ;

如果表中a都是3, b 只有一条为4, 优化器会选择b的索引进行查询,因为a的区分度不高,且还需要

进行回表操作,导致代价更高

例: select * from S where ( a between 1 and 1000) and (b between 5000 and 10000) order by b limit 1 ;

mysql 会选择哪个索引?

使用a索引需要最多扫描1000行数据,然后在进行排序

使用b索引需要最多扫描50000行数据,不需要进行排序

mysql5.7之前优化器最终会选择b索引,因为受order by的影响

在5.7之后会选择a索引

优化器确实会存在一些bug,导致选择的最终索引错误,这些内容需要进行具体sql具体分析

原则:尽量使用索引的排序,因为非索引的排序都属于filesort, 一提到文件排序其实就会比较耗时

执行器: 

拿到优化器的信息,去调用搜索引擎的api接口,先取b=4的数据,判断a是否=3,如果不等于跳过,否则放入结果集

调用接口再取b=4的下一条数据并返回执行器,重复,直到循环遍历结束

执行器讲结果集返回给客户端

注: mysql将结果返回客户端是一个增量、逐步返回的过程,不一定等所有结果查到才返回

好处:

1、服务器无需查询太多的结果,也不会因为返回太多的结果而消耗太多的内存

2、客户端也可以第一时间返回结果

 

慢sql的具体原因

磁盘io : 磁盘的访问成本大概是内存的十万倍左右,为了降低磁盘io 

在每次io时,不光把磁盘地址的数据,而且把周边的数据也读到内存缓存区

每一次读取就是一page, 具体大小在8k或者16k ,所以在读取一页数据的时候才会发生io

在查找数据的时候,B+树每一层进行一次IO,所以B+树的高度决定了io的次数

索引有最左匹配的特性

  • 哪些情况要建索引

    1、主键自动建主键索引

    2、频繁作为查询条件的字段应该创建索引

    3、查询中与其他表关联的字段,外键关系建立索引

         4、在高并发下倾向建立组合索引

         5、查询中的排序字段,排序字段若通过索引去访问将大大提高排序速度

         6、查询中统计或者分组的数据

  • 哪些情况不适合建索引

    1、频繁更新的字段

    2、where条件用不到的字段不创建索引

    3、表记录太少

    4、经常增删改的表

    5、数据重复太多的字段,为它建索引意义不大

进行explain ,速度的排序

type: system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>all

  • 索引失效_复合索引(避免)

1、应该尽量全值匹配

2、复合最佳左前缀法则(第一个索引不能掉,中间不能断开)

3、不在索引列上做任何操作(计算、函数、类型转换)会导致索引失效而转向全表扫描

4、储存引擎不能使用索引中范围条件右边的列

5、尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*

6、mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描

7、is null,is not null也会无法使用索引

8、like以统配符开头

9、字符串不加单引号

10、少用or

有些sql 中会包含force index的写法,强制去走某个索引,但条件中缺不存在这个字段,会导致全表扫描

SELECT *  FROM `coupon` FORCE INDEX (`orderid`)  WHERE `userid` = 1 AND `status` IN (0,1) ORDER BY `id` ASC ;

这个sql使用了orderid的索引,但在查询条件中却缺少orderid的字段 

数据库的主从同步

mysql主从复制需要三个线程:master(binlog dump thread)、slave(I/O thread 、SQL thread)

binlog dump线程:主库中有数据更新时,根据设置的binlog格式,将更新的事件类型写入到主库的binlog文件中,并创建log dump线程通知slave有数据更新。当I/O线程请求日志内容时,将此时的binlog名称和当前更新的位置同时传给slave的I/O线程。

I/O线程:该线程会连接到master,向log dump线程请求一份指定binlog文件位置的副本,并将请求回来的binlog存到本地的relay log中。

SQL线程:该线程检测到relay log有更新后,会读取并在本地做redo操作,将发生在主库的事件在本地重新执行一遍,来保证主从数据同步

puma , databus :

主从延迟问题

  • 主库 A 执行完成一个事务,写入 binlog,该时刻记为T1.

  • 传给从库B,从库接受完这个binlog的时刻记为T2.

  • 从库B执行完这个事务,该时刻记为T3.

是T2-T1 吗? 不是,如果网络不延迟,T2-T1 是一个很短的时间

是T3-T2吗? 是的,主要是从库执行的情况(relaylog)

具体原因:

1、从库的机器性能比主库差

2、从库的压力大

3、大事务的执行,如果是大事务,主库必须等事务完成之后才写入binlog,数据传输人到从库,执行容易产生延迟

尽量避免一次性的delete大量数据,尽量批次处理

4、主库的ddl, alter, drop, repair, create 

1、只读节点与主库的DDL同步是串行进行,如果DDL操作在主库执行时间很长,那么从库也会消耗同样的时间,比如在主库对一张500W的表添加一个字段耗费了10分钟,那么只读节点上也会耗费10分钟。

2、只读节点上有一个执行时间非常长的的查询正在执行,那么这个查询会堵塞来自主库的DDL,读节点表被锁,直到查询结束为止,进而导致了只读节点的数据延迟。

5、锁冲突

如何避免主从延迟

  1. 降低多线程大事务并发的概率,优化业务逻辑

  2. 优化SQL,避免慢SQL,减少批量操作,建议写脚本以update-sleep这样的形式完成。

  3. 提高从库机器的配置,减少主库写binlog和从库读binlog的效率差。

  4. 尽量采用短的链路,也就是主库和从库服务器的距离尽量要短,提升端口带宽,减少binlog传输的网络延时。

  5. 实时性要求的业务读强制走主库,从库只做灾备,备份。

 

posted on 2020-05-23 19:12  yingchen  阅读(680)  评论(0编辑  收藏  举报

导航