MySQL-学习总结

1.InnoDB聚簇索引和非聚簇索引的区别

InnoDB的底层是用B+树实现的,所以聚簇索引和非聚簇索引在默认情况下也是使用B+树实现,但是存在一定的差别,如下表所示:

区别 聚簇索引 非聚簇索引
叶子节点存储内容 完整数据 主键、索引列
在表中是否唯一
适用场景 范围查询、排序操作 快速查找数据

因为B+树的叶子节点之间是通过双向链表连接的,所以对于存储了数据行的聚簇索引来说在特定范围内进行数据查询和对数据进行排序操作不用修改数据的结构,IO次数减少查询速度。而对于非聚簇索引,因为存储的是主键和索引列,想要通过非聚簇索引来查找完整的数据内容会增加回表的次数,造成IO次数的开销,因此查找完整的数据比较,但是用于快速查找特定数据,根据主键和索引列匹配数据会比聚簇索引快。

2.存储引擎有哪些?它们之间有什么区别?

常见的存储引擎有MyISAM、InnoDB、Memory

在MySQL 5.5版本之前,默认的存储引擎是MyISAM

在MySQL 5.5版本之后,默认的存储引擎变为了InnoDB

MemoryMySQL 3.23版本中引入的使用内存缓存数据,将索引存储在磁盘上的一种引擎。

在日常使用过程中我们主要用到前两种引擎,二者之间有很大差别,主要包括:

  1. InnoDB支持事务,MyISAM不支持事务。
  2. InnoDB最小的锁粒度为行级锁,MyISAM只支持表级锁。
  3. InnoDB数据支持聚簇索引和非聚簇索引存储,MyISAM只支持非聚簇索引。
  4. InnoDB支持外键关联,MyISAM不支持外键。
  5. InnoDB支持MVCC多版本并发控制机制,MyISAM不支持MVCC。

3.索引类型分类

从数据结构角度划分为:

  • B+树索引
  • Hash索引
  • 倒排(全文)索引
  • R树索引

从底层B+树角度划分为:

  • 聚簇索引
  • 非聚簇索引

从物理存储层级角度划分为:

  • 主键索引
  • 二级索引

从索引的性质角度划分为:

  • 主键索引
  • 普通索引
  • 前缀索引
  • 唯一索引

从字段个数角度划分为:

  • 联合索引
  • 单排索引

4.三层B+树能存储的数据大小

要知道三层B+树能存储多少数据得从结构说起,

首先,三层B+树的结构分别为:

  • 第一层:根节点
  • 第二层:中间层
  • 第三层:叶子节点

其中,叶子节点存储的是数据,(非叶子节点)的根节点中间层存储的是索引+指针

三层分别计算:

  • 根节点:根节点存储的是索引和指针,因为根节点只有一个,像伞状的线指向下面的分支,指针就是线,而指针的最大密度也就是指针的数量最大为1170,因此根节点指向中间层的最大数量为1170

  • 中间层:和根节点一样,指向下一层的最大数量就是指针的最大容量,即为1170。

  • 叶子节点:InnoDB的默认页大小为16KB,假设一条数据的大小为1KB(实际使用过程中比1KB小,这里取整数最大值计算),那么一个叶子节点存储的数据条数=16/1=16条

即可得出:三层B+树存储的数据大小为:1170X1170X16=21902400——2000W条

这里得出的是单表的最大存储数量,在实际应用中最好不要超过500W条,控制在500W以下能够保证数据存储和查询的最快时间复杂度。

5.索引的最左前缀匹配原则

索引的最左前缀匹配原则指的是:在使用的联合索引有多个列的时候

我们的select * from tablename where xx = mm;这里的xx条件必须从最左侧开始匹配

xx要一一对应,例如:

1
select * from tablename where a=1,b=2,c=3
1
select * from tablename where a=2,b=3,c=1

如果没有对应则不符合最左匹配原则,导致索引失效;而且不能进行范围查询,比如:

1
select * from tablename where a>1,b=2,c=3

范围查询也会导致导致索引失效。

6.为什么使用B+树作为索引结构?

使用B+树作为索引有以下几点优势:

  • B+树除了叶子节点外都存储的是索引和指针,比起其他的树结构能够存储更多的索引数量,而叶子节点存储的是数据,叶子节点之间采用双向链表进行链接,用户在进行索引的插入和删除操作时B+树的结构能够自平衡,结构稳定,IO次数也能有效的减少,而且在进行范围查询的时候效率也更高。

7.建立索引的注意事项

在给表建立索引时,要注意以下几点:

  1. 注意索引的数量,根据使用情况适量增加索引,而不是越多越好。
  2. 对于存储长字段的类型不要加索引,会导致效率减少,比如(text、longtext字段类型)。
  3. 对于可以让选择选择的大量重复的字段,比如性别(男/女)、(是/否)这种,不要加索引,(不一定,有时候得具体情况具体分析)。
  4. 能用联合查询的情况尽量用联合索引,减少索引数量,节约空间。
  5. 对于经常使用(分组、排序、去重)等操作的字段加索引。

8.回表是什么?

MySQL中的回表是值在使用二级索引的时候,需要根据主键去匹配数据,再进行查询操作的过程,二级索引存储的是主键和指针,要获得完整的数据得通过主键去聚簇索引中查询,这个过程就是回表。

因为进行了多次查询,所以IO次数增加,查询的效率会变慢,解决这个问题可以使用覆盖索引,让第一次查询时要查询的结果在第一次查询时得到,就不用进行下面的查询操作,增加效率。

9.使用索引一定有效吗?如何排查索引效果?

使用索引不一定有效,对查询语句使用EXPLAIN命令可以知道当前查询语句是否采用了索引,当EXPLAIN返回的列type显示indexrange代表使用索引,而key可以看到使用的索引的类型是什么,如果keynull也表示没有使用索引。

以下操作都会导致索引失效:

  1. 对索引使用函数
  2. 对索引使用运算符
  3. 在使用联合索引的时候不符合最左前缀匹配原则
  4. 在使用模糊查询除了LIKE "xx%"的情况
  5. 使用OR条件的时候,两边有一方不是索引的情况
  6. 参数类型不匹配的情况
  7. 使用order by后面不是主键或者覆盖索引的情况
  8. 当表字段较少的情况可能进行全表扫描,不会使用索引

10.索引数量是否越多越好?

索引数量不是越多越好的,可以从两方面分析:

从空间角度上看:

  • 因为索引一般采用B+树结构,一个数据页的大小为16KB,索引的数量过多的话会造成空间资源的消耗。

从时间角度上看:

  • 某个表中索引数量过多效率却不一定提升,而且在建立和维护索引的时候,需要消耗时间,造成时间开销。

11.如何使用EXPLAIN语句进行查询分析

SELECT语句开头使用EXPLAIN可以查看当前SQL语句的执行信息,对执行过程进行分析。

EXPLAIN执行的语句并不是真正的执行,而是通过查询优化器进行分析。输出的信息如下:

字段名 解释
id 查询的序列号,表示执行顺序
select_type★ 查询类型,如 SIMPLE(简单查询)、PRIMARY(主查询)、SUBQUERY(子查询)等
table 正在访问的表名。
type★ 访问类型,表示查询使用的访问方法
possible_keys 查询中可能使用的索引
key★ 实际使用的索引
key_len 使用的索引长度
ref 显示索引的哪一列被使用了
rows★ 估计需要读取的行数(越小越好)
Extra★ 额外信息,如 Using where(使用了 WHERE 过滤)、Using index(使用了覆盖索引)、Using temporary(使用了临时表)、Using filesort(使用了文件排序)等

其中type(访问类型)字段查询效率为:

system>const>eq_ref>ref>range>index>ALL

system:表仅包含一行记录(系统表),MySQL客户端启动时自动加到到内存,不需要额外IO操作,性能最优

const:单行匹配,通过主键或唯一索引的等值查询,查询结果为常量

eq_ref:对于每个来自前表的行组合,精确匹配唯一索引的一行,通常用于多表连接

ref:非唯一索引扫描,返回匹配某个单独值的所有行,适用于普通索引的等值查询

range:索引范围扫描,检索特定范围内的行,如使用 BETWEEN、>、< 等操作符。

index:全索引扫描,遍历整个索引

ALL:全表扫描

12.如何进行SQL调优?

首先最直接的是通过用EXPLAIN来分析慢SQL(查询时间超过默认阈值的SQL语句,一般是10S,可以通过set global long_query_time = xx来修改),查看造成慢SQL的原因对应优化

慢SQL命令:

1
set global slow_query_log ='ON' //开启Mysql记录日志,默认关闭
1
show variables like %slow_query_log% //查询慢SQL是否开启以及存储路径
1
set global long_query_time = xx //设置慢SQL阈值

可以从以下几点对SQL语句进行优化(避免索引失效):

  1. 避免对索引使用函数
  2. 避免对索引使用运算符
  3. 在使用联合索引的时候要符合最左前缀匹配原则
  4. 避免在使用模糊查询除了LIKE "xx%"的情况
  5. 避免在使用OR条件的时候,两边有一方不是索引的情况
  6. 避免参数类型不匹配
  7. 避免在使用order by后面不是主键或者覆盖索引的情况

除了SQL语句的优化还可以从缓存业务方面进行优化

13.varchar和char的区别

在Mysql中,varchar和char都是用来存储字符串的类型区别在于,char的长度固定,而varchar的长度是可变的,具体区别如下:

  • 分配方式

    • char在使用时,一次性分配好空间,当我们填充好字段后,如果有剩余的空间则用空格填充满,innodb在调用时会忽略char里面的多余空格字符;而varchar类型是根据我们输入的字符串长度动态的分配空间,会在我们输入的字符串的长度基础上增加1-2个字节用来记录字符串长度信息
  • 最大长度

    • varchar的最大长度是65535字节
    • char的最大长度是255字符
  • 效率

    • varchar存储的灵活性强,但效率低;char效率高,但浪费存储空间(实际上浪费不了多少)

一般情况像存储18位长度身份证ID/11位手机号这种固定长度信息的字段推荐使用char类型,而比如用户名/email这种长度变化的字段推荐使用varchar存储。

14.count(*)、count(1)和 count(字段名) 有什么区别?

  • count(*):统计的是所有行的数量包括null值

  • count(1):统计的也是所有行的数量包括null值

  • count(字段名):统计的是非null字段的行数,正常情况需要进行null的判断,效率更低

  • 在需要统计null的时候只能使用count(*)、count(1)

  • 在不需要统计null的时候只能使用count(字段名)

  • 所以不需要考虑性能问题

15.B+ 树中查询数据的全过程

  1. B+树有三层结构:根节点、中间层、叶子节点
  2. 首先,数据是从根节点开始查找,根节点中存储的是索引+指针,根据比较数据键值和节点中存储的索引,确定数据具体在哪个区间,从而确定在的分支
  3. 根节点确定好分支之后再从中间层进行从左到右,从上到下依次遍历,从而确定对于叶子节点的分支
  4. 叶子节点存储实际的数据行记录,一页的大小为16KB ,因此存储的数据行不止一条
  5. 叶子节点中数据行以组的形式划分,叶子节点有页目录结构,第一个槽存储的是最小记录中间存储的是分组,最后一个槽存储最大记录,页目录实际上是一个索引,是用来快速找到记录的
  6. 通过二分法可以定位到组,最后利用链表遍历就可以找到对应的数据行

16.事务是如何实现的

Mysql中事务的ACID是

  • 原子性
  • 一致性
  • 隔离性
  • 持久性

主要通过redulog、undolog、锁、MVCC来实现事务:

  1. 原子性(A):由undo log回滚日志记录事务的反向操作,通过保存数据的历史版本将事务执行失败后进行恢复。同时也实现了隔离性
  2. 一致性(C):通过实现AID来满足一致性
  3. 隔离性(I):通过锁机制(行锁、间隙锁等)和MVCC多版本并发控制机制满足非锁定读的需求,实现读已提交和可重复读两种隔离级别以及事务隔离性
  4. 持久性(D):由redo log重做日志记录事务对数据库的修改用于恢复未提交的更改满足持久性

17.日志类型有哪些?binlog、redo log 和 undo log的作用和区别是什么

Mysql中日志类型有三种:

  1. redulog(重做日志):用于记录事务对数据库的修改操作并写入磁盘,数据库宕机后,通过重放可以恢复未提交的更改,实现了事务的持久性。
  2. undolog(回滚日志):用于记录事务的反向操作,在事务执行失败后进行回滚,撤销已提交的更改,恢复到开始的状态,实现了事务的隔离性。
  3. binlog(二进制日志):二进制日志文件,用于记录mysql的所有insert、update、delete操作,包括DDL(数据库表结构变化)和DML(数据库表的数据插入更新修改删除等变化),可以将数据库还原到某个成功提交后的状态,可以用来实现主从复制

区别在于:

  1. 作用不同:redolog用于实现持久性和一致性,而binlog用于记录数据库所有操作
    redolog用于恢复数据,undolog用于回滚操作
  2. 类型不同:binlog是巡逻日志,redulog是物理日志记录数据页的修改
  3. 范围不同:binlog可以跨平台,redolog不能

18.MVCC 是什么?

MVCC(Multi-Version Concurrency Control)是一种多版本并发控制机制,核心思想是通过维护数据在不同时间点的多个版本,使得读操作在大多数情况下无需加锁,实现读写操作的并发执行,提高数据库性能和吞吐量

主要原理:

  1. 数据被某个事务修改插入时,会关联一个事务 ID
  2. 在更新或删除时,数据库并不会物理地覆盖原来的数据,而是通过undolog来保留之前的版本信息,这样就形成了多个版本
  3. 由于行记录会包含或关联该行在不同时刻的多个版本,其他事务在读取这行数据时,可以根据自己的隔离级别和当前事务的版本号,去选择合适版本的行数据,而不会和写操作阻塞

ReadView(读视图),包含下列信息:

  1. 目前活跃事务的 ID 列表
  2. 下一个将要分配的事务 ID
  3. 事务自己的 ID 等信息

当事务开始时,数据库会创建一个ReadView,当进行读操作时,判断每条记录的创建版本和删除版本是否满足当前读视图的可见性,具体如下:

  1. 如果记录的版本号<当前事务启动时的最小活跃事务 ID,则这个版本对当前事务可见
  2. 如果记录的版本号>当前事务启动时的最大活跃事务 ID,则对当前事务来说是不可见的,需要往前找更旧的版本
  3. 如果记录的版本号在当前事务的活跃 ID 列表里,则也不可见,因为它还处于其他事务未提交状态,需继续往前找前一个版本

通过这种机制,读取数据时就不需要加锁,只需根据读视图判断哪一个版本对当前事务可见即可,提高数据库性能和吞吐量

19.MySQL中事务隔离级别有哪些?

事务隔离级别有4种:

  1. 读未提交:该级别下,一个事务可以看到另一个事务未提交的数据修改,可能会导致脏读问题

  2. 读已提交:该级别下,一个事务只能看到已经提交的其他事务的修改,可以避免脏读问题,但是可能会产生不可重复读问题

  3. 可重复读:Mysql中Innodb引擎的默认隔离级别,该级别下,一个事务执行中查询到的数据和事务启动时看到的数据一致,可以避免不可重复读问题,但是可能会产生幻读问题

  4. 串行化:该级别下,事务串行执行,如果发生了读写冲突,后访问的事务必须等前一个事务执行完成才能执行,可以解决所有的并发问题

脏读:一个事务读取到另一个事务未提交的数据,如果如果这个事务最后没有成功提交,而是回滚,那么事务读取到的数据就是脏的

不可重复读:在同一个事务中,读取同一个数据两次,由于其他事务修改,两次读取的结果都不同

幻读:在同一个事务中,多次查询数据,如果出现查询到的记录数量不一致的情况,就像产生幻觉

隔离级别从小到大排序:

  • 读未提交<读已提交<可重复读<串行化

MySQL InnoDB 引擎的默认隔离级别是可重复读,很大程度上避免幻读现象,但不能完全避免,以下两种方案可以针对快照读当前读解决幻读:

快照读:通过MVCC多版本并发控制解决幻读

当前读:通过next-key lock(记录锁+间隙锁)解决幻读

20.MySQL中脏读、不可重复读和幻读分别是什么?

脏读:一个事务读取到另一个事务未提交的数据,如果如果这个事务最后没有成功提交,而是回滚,那么事务读取到的数据就是脏的

不可重复读:在同一个事务中,读取同一个数据两次,由于其他事务修改,两次读取的结果都不同

幻读:在同一个事务中,多次查询数据,如果出现查询到的记录数量不一致的情况,就像产生幻觉

  • 读未提交隔离级别下产生脏读
  • 读未提交和读已提交下产生不可重复读
  • 读未提交读已提交和可重复读下产生幻读
  • 串行化都不产生

21.MySQL 默认的事务隔离级别是什么?为什么选择这个级别?

InnoDB 存储引擎的默认支持的隔离级别为可重复读(Repeatable Read)

MySQL 8.0之前可以通过SELECT @@tx_isolation;命令来查看
MySQL 8.0 改为SELECT @@transaction_isolation;

可重复读在 MySQL InnoDB 中通过 Next-Key Lock(间隙锁)机制,避免了在 statement 格式 binlog 下可能出现的“先插入再删除”的主从不一致问题。

主从不一致问题产生的场景

1
事务 1:DELETE FROM table WHERE a < 10;(不提交)
1
事务 2:INSERT INTO table(a) VALUES (5);(提交)

在 statement 格式的 binlog 下,binlog 会按提交顺序记录 SQL:

  • 事务2 比事务1先提交,因此从库会先执行 INSERT,再执行 DELETE

REPEATABLE READ 隔离级别下

在执行DELETE FROM table WHERE a < 10 时,会采用 Next-Key Lock(间隙锁 + 行锁)的方式,将 ( -∞, 10 ) 的这段区间都锁住。

此时 T2 想要插入 a=5,就会被这个间隙锁所阻塞,必须等待 T1 的 DELETE 提交或回滚后才能继续。

逻辑上先执行 DELETE 的事务,在物理上就会先提交,binlog 里先记录 DELETE再记录 INSERT,主从库重放顺序一致,保证了数据一致性。

22.MySQL有哪些锁的类型?

  1. 行级锁:针对特定的行加锁,允许其他事务并发访问不同的行,适用于高并发场景,InnoDB 引擎支持行级锁,而 MyISAM 引擎不支持行级锁,InnoDB 最重要的就是实现了颗粒度更细的行级锁
  2. 表级锁:对整个表加锁,其他事务无法对该表进行任何读写操作,适用于需要保证完整性的小型表,释放表锁可以使用unlock tables会释放当前会话的所有表锁
  3. 意向锁:是一种表锁,用于表示某个事务对某行数据加锁的意图,又可以分为意向共享锁/意向排他锁,主要用于行级锁和表级锁的结合,意向锁的目的是为了快速判断表里是否有记录被加锁
  4. 共享锁:允许多个事务并发读取同一资源,但不允许修改,只有在释放共享锁之后其他事务才能获取排他锁
  5. 排他锁:只允许一个事务对资源进行读写,其他事务在获得排他锁之前无法访问该资源,能解决数据修改和删除的问题
  6. 间隙锁:对间隙加锁分为X 型间隙锁S 型间隙锁,用于锁定索引范围之间的间隙,以避免其他事务在这个范围内插入新的数据,可以用来解决幻读问题,一般是和排它锁一起加上的,间隙锁虽然存在 X 型间隙锁和 S 型间隙锁,但是并没有什么区别间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系
  7. 临键锁:是行级锁和间隙锁的结合,锁定具体行和行前面的间隙,并且锁定记录本身,确保在一个范围内不会出现幻读,间隙锁之间是兼容的,两个事务可以同时持有包含共同间隙范围的间隙锁,不存在互斥关系
  8. 元数据锁:用于保护数据库对象(如表和索引的元数据),防止在进行DDL操作时其他事务对这些对象进行修改
  9. 插入意向锁:特殊的意向锁,插入意向锁名字虽然有意向锁,但是它并不是意向锁,它是一种特殊的间隙锁,属于行级别锁,用于在某个拥有间隙锁的事务提交,释放间隙锁的期间生成插入意向锁,表明有事务想在某个区间插入新记录,但是现在处于等待状态,阻止其他的排他锁
  10. 自增锁:特殊的表锁,在插入数据时,会加一个表级别的自增锁,保证自增ID的唯一性,锁不是一个事务提交后才释放,而是在执行完插入语句后立即释放,自增锁对大量数据进行插入的时候,会影响插入性能,因为另一个事务中的插入会被阻塞

表级锁:

  1. 表锁
  2. 意向锁
  3. 元数据锁
  4. 自增锁

行级锁:

  1. 间隙锁
  2. 临键锁
  3. 插入意向锁

23.MySQL中如果发生死锁该如何解决?

什么是死锁?

mysql中死锁是指两个或者两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,死锁的产生经常和间隙锁有关

死锁产生的四个必要条件

  1. 互斥:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程释放
  2. 请求保持:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己获得的其它资源不释放
  3. 不可剥夺:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放
  4. 循环等待:指在发生死锁时,必然存在的资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源

通过show engine innodb status指令查看最近一次死锁信息

解决死锁的方式

  1. 等待,直到超时(innodb_lock_wait_timeout=50s),当等待时间超过阈值就会释放锁,进行回滚
  2. 发起死锁检测,主动回滚一条事务,让其他事务继续执行(innodb_deadlock_detect=on),一般回滚资源最少的事务
  3. 如果需要手动解除死锁,有一种最简单的方式,就是找到线程id之后,直接kill掉

避免死锁

死锁产生有四个必要条件,其中互斥在我们使用mysql中是不可不避免的,因此我们可以从剩下的三个条件入手,尽量避免死锁或降低死锁产生的几率,方式如下:

  1. 不同的事务访问同一组表时,尽量以相同的顺序访问各表,可以明显的减少死锁的发生
  2. 在主键等值更新的时候,尽量先查询数据库中是否有满足条件的数据,存在才更新,否则如果去更新一条数据库不存在的数据,一样会产生间隙锁。
  3. 尽量使用主键更新数据,主键是唯一索引,在等值查询能查到数据的情况下只会产生记录锁,不会产生间隙锁,产生死锁的概率就减少了
  4. 避免大事务,小事务发送锁冲突的几率小
  5. 在允许幻读和不可重复度的情况下,尽量使用RC的隔离级别,避免间隙锁造成的死锁,因为产生死锁经常都跟间隙锁有关,间隙锁的存在本身也是在RR隔离级别来解决幻读的一种措施。
  6. 开启死锁检测,适当调整锁等待时间

24.MySQL 事务的二阶段提交是什么?

Mysql事务的二阶段提交是指在Mysql中,为了保证事务的持久性和一致性确保redolog和binlog的数据一致,使用的一种机制,通过二阶段提交可以保证在崩溃后进行可靠的恢复

二阶段提交的核心流程,以 InnoDB 存储引擎为例:

  • 准备阶段
    • InnoDB 执行事务中的所有更新操作,将更新先写入内存中并生成相应的redolog/undolog日志
    • 事务提交时,Innodb会先写入redolog,并将状态标记为prepare,表示事务已经准备提交但还未真正完成,此时的redolog是预提交状态,还未标记为完成提交
  • 提交阶段
    • Mysql Server层会在 binlog 中记录该事务的所有修改,然后将 binlog 写入磁盘进行记录
    • 写入磁盘成功后,Mysql Server层再通知 Innodb将事务的 redolog 状态从 prepare改为 commit,完成最终提交
    • 此时,如果再次发生崩溃,则 redolog和undolog 都已同步写入磁盘,可以保证事务的完整性,并且主从复制也可以按照完整的 binlog 恢复相同数据

Mysql 的二阶段提交在本质上是保证binlog和Innodb、redolog之间数据的原子性和一致性

以便在数据库崩溃或进行主从复制时,能够做到事务恢复和保持数据一致

25.MySQL 中如何解决深度分页的问题?

  1. 覆盖索引并使用子查询/Join 限定范围

    • 方法:

      内层的子查询查出需要的主键 id ,通过索引进行快速定位。然后通过主键与外层表关联join得到完整行数据,可以充分利用索引覆盖扫描,在深度分页时只读取少量列的索引来定位范围,速度快,io操作减少

    • 栗子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      SELECT a.* FROM ikun AS a
      JOIN (
      SELECT id
      FROM ikun
      WHERE ##其他条件
      ORDER BY id
      LIMIT 100000, 20
      ) AS b ON a.id = b.id
      ORDER BY a.id;
  2. 通过记录上一次查询的最大/最小主键值

    • 方法:

      通过记录上一次查询的最大/最小主键值,在下一次查询时直接基于这个主键值进行条件限制

    • 栗子:

      1
      SELECT * FROM  ikun WHERE  id > #{last_id} ORDER BY id ASC LIMIT  20
  3. 适当限制可跳转的页码或总页数

    • 可以从根本上解决深度分页问题
  4. 使用elasticsearch

    • 不太会就不考虑了

26.什么是 MySQL的主从同步机制?它是如何实现的?

原理:

  • MySQL的主从同步机制是一种数据复制技术,用于将主数据库上的数据同步到一个或多个从数据库中,保持数据一致的一种机制,主主从同步的核心在于binlog日志,它记录了所有数据变化的历史,主数据库在执行写操作时,会将这些操作记录到 binlog 中,然后推送给从数据库从数据库重放对应的日志完成数据的复制,通过主从同步机制,可以实现读写分离、负载均衡、数据容灾、故障转移等功能

主从同复制类型:

  1. 异步复制(Mysql默认的):主库不用等从库响应的执行结果,只要主库将事务写入 binlog,就算提交成功
    • 优点:写操作响应快,主库压力小
    • 缺点:如果主库崩溃,可能会出现数据不一致的情形
  2. 同步复制:主库需要同步等待所有从库确认收到数据
    • 优点:数据一致性高
    • 缺点:写操作响应慢
  3. 半同步复制:主库在事务提交时,至少要等待一个从库接收到该事务的 binlog 并写入 Relay Log 后,才算提交成功
    • 优点:比异步更安全,可减少数据丢失
    • 缺点:写操作延迟增加,吞吐量有所下降

27.如何处理 MySQL 的主从同步延迟?

主从同步延迟和我们日常打游戏的延迟一样,只能减少,不能避免

主要从几个方面减少延迟:

  1. 使用二次查询:如果从库查不到数据,则再去主库查一遍,缺点是主库压力变大
  2. 强制将写之后立即读的操作转移到主库(不推荐)
  3. 关键业务读写走主库,非关键业务读写分离
  4. 使用缓存:主库写入后同步到缓存中,查询时可以先查询缓存,避免延迟问题,但是会导致缓存数据一致性的问题
  5. 提高从库的硬件设备和网络带宽(有效提升)
  6. 拆分大事务:避免执行大事务,将其拆分为多个小事务,减少单个事务对主从同步的影响
  7. 负载均衡:增加从服务器的数量,分散读取请求,降低单个从服务器的负载