面试系列(二):mysql

525 阅读33分钟

mysql进行分库分表后如何进行分页查询

MySQL 分库分表后实现高效分页查询是分布式架构下的核心挑战,以下是关键方案及适用场景对比:

一、分页查询方案对比

方案实现原理优点缺点适用场景
全局查询法每个分片执行相同分页 SQL(如 LIMIT offset, size),内存合并结果后全局排序截取14实现简单,兼容任意排序字段深度分页性能差(offset 增大时扫描全表);数据合并精度低(跨库数据可能重复/缺失)45小数据量(总结果 < 1000 条)
禁止跳页法仅支持“上一页/下一页”:记录上一页最大 ID(或时间戳),下页查询条件为 WHERE id > last_max_id ORDER BY id LIMIT size69避免 offset 扫描,性能稳定无法跳转任意页码C端产品(如APP无限滚动)
分片键分页法利用分片键(如用户ID、时间)过滤数据:WHERE shard_key = X ORDER BY time LIMIT offset, size7直接命中单个分片,无合并开销依赖业务连续性(如按时间分片需时间有序)7分片键明确的查询(如查某用户订单)
二次查询法第一次‌:各分片并行查询 LIMIT offset/N, size(N=分片数);‌第二次‌:根据第一次结果的最小边界值,精确查询目标数据56精准返回目标数据,避免深度分页扫描需两次查询,逻辑复杂要求精确跳页的B端系统
子查询优化法先查主键再关联:SELECT * FROM table JOIN (SELECT id FROM table ORDER BY id LIMIT 100000,10) tmp ON table.id=tmp.id314减少回表,提升深分页性能仅适用于单库分表,不解决跨库问题14单库分表场景(非分库)

二、深度分页陷阱与解决方案

  1. ‌**LIMIT offset, size 性能崩塌**‌

    • 原因‌:offset 增大导致扫描数据量指数级上升(如 offset=100000 需扫描 10w+size 行)13。

    • 优化‌:

      • ✅ ‌禁止跳页法‌(消除 offset
      • ✅ ‌子查询优化‌(先缩小主键范围)
      • ⚠️ 避免无条件 ORDER BY RAND()9
  2. 跨库排序精度问题

    • 案例‌:按时间排序时,各分片返回局部有序数据,合并后全局乱序(如分片1时间=[1,3,5],分片2=[2,4]→合并[1,2,3,4,5])56。

    • 方案‌:

      • 二次查询法(第一次确定全局边界)6
      • 业务折衷:仅按分片键排序(如用户ID+时间)7

三、选型建议

场景推荐方案示例语句
用户中心(按UID分片)分片键分页法SELECT * FROM user_orders WHERE uid=123 ORDER BY time LIMIT 50,107
电商后台(任意跳页)二次查询法流程参考6
实时数据流(APP瀑布流)禁止跳页法SELECT * FROM feed WHERE id > last_id ORDER BY id LIMIT 209
日志查询(时间范围分片)分片键分页法SELECT * FROM log_2025 WHERE day="06-12" ORDER BY time LIMIT 0,1007

四、高级优化技巧

  1. 全局索引表

    • 建立独立索引库,存储所有分片的主键及排序字段,先查索引再路由到分片14。
  2. 读写分离

    • 分页查询走从库,避免主库性能抖动11。
  3. Elasticsearch 替代

    • 复杂条件分页用 ES 聚合结果,MySQL 仅存储原始数据10。

mysql的binlog有哪些类型

MySQL Binlog 主要有三种核心格式,它们在记录方式、应用场景及特性上有显著差异:

一、Binlog 格式类型对比

格式记录方式优点缺点适用场景
STATEMENT记录执行的 SQL 语句(如 UPDATE t SET col=1)12日志量小,节省存储与网络资源;可读性强,便于审计15非确定性函数(如 NOW()UUID())易致主从不一致;批量操作可能锁升级17SQL 简单且无需强一致性的场景
ROW记录每行数据的变更细节(修改前后的值)12数据精准,避免主从不一致;支持复杂操作(如 LOAD_FILE())18日志量大(尤其 ALTER TABLE 等操作);恢复效率低15金融等高一致性要求的场景
MIXED动态混合:默认 STATEMENT,检测到风险时切 ROW17平衡性能与一致性;灵活适配 SQL 类型18逻辑复杂,可能引入隐式兼容性问题1通用业务(兼顾性能与一致性)17

二、关键特性详解

  1. STATEMENT 风险案例

    sqlCopy Code
    UPDATE orders SET create_time = NOW();  -- 主从服务器时间不同则数据不一致:ml-citation{ref="1,7" data="citationList"}
    
  2. ROW 日志膨胀场景

    • 执行 ALTER TABLE 修改大表结构时,ROW 格式需记录每一行变更,产生海量日志57。
  3. MIXED 自动切换逻辑

    • 当 SQL 包含 UUID()RAND() 等非确定性函数时,自动转为 ROW 格式记录17。

三、配置建议与优化

  1. 格式选择优先级

    • 强一致性需求 → ‌ROW
    • 通用场景 → ‌MIXED
    • 日志敏感型 → ‌STATEMENT‌(需规避风险函数)17
  2. 日志管理优化

    • 限制单文件大小:max_binlog_size
    • 定期清理过期日志:PURGE BINARY LOGS17

总结

MySQL Binlog 的三种格式本质是 ‌ “日志精度” ‌ 与 ‌ “系统开销” ‌ 的权衡:

  • STATEMENT‌ 轻量但风险高,‌ROW‌ 安全但资源消耗大,‌MIXED‌ 是折中方案17。
  • 生产环境推荐 ‌MIXED‌,金融等关键业务强制使用 ‌ROW‌17。

mysql执行一条sql语句的流程是怎样的?

MySQL执行一条SQL语句的基本流程如下:

  1. SQL语句解析:MySQL接收到SQL语句后,首先进行语法解析,检查SQL语句是否符合MySQL语法规范,如果SQL语句不符合规范,MySQL会返回一个错误信息。
  2. 语义分析:在语法分析之后,MySQL会对SQL语句进行语义分析,主要是检查SQL语句中的表、列、函数等是否存在,以及访问权限是否符合要求。如果存在错误,MySQL也会返回相应的错误信息。
  3. 查询优化:MySQL会对查询进行优化,选择最优的查询策略,例如选择最优的索引等。
  4. 执行查询:当查询优化完成后,MySQL会执行查询,从磁盘或内存中读取数据,并根据查询条件进行过滤和排序等操作。
  5. 返回结果:最后,MySQL会将查询结果返回给客户端。

需要注意的是,MySQL在执行SQL语句时,还涉及到并发控制、锁等问题,以保证数据的一致性和安全性。此外,MySQL还提供了多种类型的存储引擎,不同的存储引擎有不同的实现方式和性能特点,因此在实际应用中,需要根据具体需求选择合适的存储引擎。

sql执行过程中为什么要加缓存?默认是开启还是关闭?

在MySQL中,缓存是一种提高性能的重要手段,可以大大降低对磁盘的IO访问,提高查询效率。具体来说,MySQL中常见的缓存有查询缓存、InnoDB缓冲池、MyISAM键缓存等。

默认情况下,MySQL会开启查询缓存,但这个缓存机制已经被认为是不太适合大型高并发系统,因为它的效率很低,而且容易出现缓存失效的情况。因此,从MySQL 8.0开始,查询缓存已经被废弃。

相反,MySQL使用了更为高效的InnoDB缓冲池,它是一个内存缓存,用于缓存磁盘上的数据和索引。MyISAM键缓存则用于缓存MyISAM表的索引数据。

总的来说,缓存在MySQL中是一个非常重要的性能优化手段,它可以提高查询效率,降低系统负载。在实际使用中,应该根据具体的应用场景和数据量大小,选择合适的缓存机制,并合理调整缓存大小和缓存策略,以达到最优的性能表现。

请大概讲一下mysql中innodb的索引结构?

首先,我们知道的是,mysql中的innodb的索引结构是b+树。

  一般来说,在索引结构中,b+树的每一个节点都是一个数据页,什么是数据页呢?数据页是innodb引擎中的定义的一个内存逻辑结构,它规定了我们的存储引擎,每次从磁盘中读取数据的时候,最少也要读取一页的数据,也规定了数据从内存刷新到磁盘的时候最少也是要刷新一页的数据,数据页的大小大概是16kb,innodb规定了每一个数据页至少可以存储两条数据。

在数据页中,数据是根据主键id进行排序的,并且这个数据页中的多条数据,会形成一个单向链表。

  那么他们是怎么组成一个b+树的呢?

  数据页会分成了不同的类型,我们经常说到的两种类型,一种是目录项数据页,一种是用户记录数据页。

  目录项数据页和用户记录数据页的不同在哪里?目录项数据页存储的数据,是只有主键id和这条数据指向的数据页的页码这两列。

  而用户记录数据页存储的数据的列,就是我们自己定义的数据库表的列。

  换句话说,在我们b+树中的叶子节点,就是我们用户记录数据页,而非叶子节点,就是我们的目录项数据页。

  同一层级的b+树节点按照顺序,组成了一个双向链表。

  刚刚我讲述的这种索引结构,是我们任何一个mysql数据表都会有的一个聚簇索引,那么在mysql中还有其他类型的索引,比如说二级索引。那么这种索引的数据结构是什么样呢?

  当然这种索引的数据结构也同样是b+树,但是这种索引和我们刚刚说的聚簇索引是不一样的。刚刚我们讲到了,聚簇索引的非叶子节点是只有两列,主键id和指向的下一个层级的数据页的页码,那么在普通索引中,我们的非叶子节点的这个数据页的列,数量是不确定的,主要是看我们的索引列有多少个,这个数据页的列是我们的索引列加上指向下一层级节点的数据页的页码。而我们的叶子节点,存储就是我们的索引列加上我们的主键id。

  也因此,在我们实际的查询中,如果使用了普通索引,那么我们在这个普通索引上进行了查询之后,是没有办法查询到这条数据的所有列的,还需要拿到主键id之后,到聚簇索引这里进行回表操作,拿到这条数据的所有的数据列。

 

Explain查询出来的都有哪些列?都是什么含义?

主要有的列有几种,我记不大全了,就大概说一下。

第一个列是id列,主要是mysql分配给一个select关键字的id。第二个列是table列,是对应着的这个select关键字查询的是哪一个表。然后就是type列,type列主要表示的是这个查询时的访问方法,比如有system,const,ref,index,all等等。

接下来时possible_key,这个值时表示着我们的查询可能会用到哪些索引,还有一个列是key,key的意思是说明我们的查询实际使用的索引是哪些,这个值的判断,是根据我们的执行计划里面的成本分析的来的。

还有rows列,就表明我们的mysql估算出的满足查询条件的数据的数量。

还有key_len字段,这个字段实际上是sql语句在查询的过程中,形成扫描区间的字段的占据的存储长度。

 

为什么使用B+树当索引结构?相对于其他数据结构而言,优点在哪?

  为什么使用B+树这种数据结构当做我们索引的结构呢?

  我们知道,索引本身是很大的,不可能全部存储到内存中,因此索引往往通过索引文件的像是存储到磁盘上,那么这样的话,索引在查询的过程中,会产生I/O操作,所以评价要给索引好坏的一个指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。

  也就是说,通过索引去查询速度,性能要达到最佳。

  我们当然可以选择其他的数据结构来当mysql的索引,但是要考虑到索引是否是查询最快且占用的磁盘空间最小。

  基于查询岁都最快这一点,我们先看二叉查找树,二叉树的特点是任何节点的左子节点都要小于右子节点,右子节点都要大于当前节点,但是二叉查找树在极端的情况下会降级为链表结构,效率太低,而且二叉查找树本身的高度相对于b+树而言,还是太高了,需要查找到数据的IO次数太多了。

  那么接下来我们说是可以使用二叉平衡树,二叉平衡树的特点就是在二叉查找树的基础上,任何一个节点的左右子树的高度差不能超过1,但是它的问题同样很明显,一是二叉平衡树也是太高了,另外一个点,每一次插入节点或者删除节点的时候,二叉平衡树的平衡旋转也是很耗时间的。

  我们还可以选择红黑树,红黑树的特点是从根到叶子节点的最长路径,不能比最短路径多两倍。但是红黑树相对于B+树而言,还是太高了,而且红黑树的每个节点都只能存储一个键值和数据,所需要磁盘空间太大了。

  那么,我们为什么最终使用的是b+树而不是b树呢?

  B树和b+树都有着基本相同的特性,比如每一个节点都有多个子节点,没有限制子节点的个数,基于这一点,在查询数据的时候,IO的次数必然会减少,但是B树是任何一个节点不止可以存储键值对,而且还可以存储数据的,这样的话消耗的磁盘空间就比较大了,而b+树则是只会在叶子节点存储数据,其他节点都只会存储键值对,这样对磁盘空间的消耗最少,而且每次读取数据到内存中时候,每次读取到的节点的数量也是最多。

  所以,这也是为什么mysql的innodb的索引最终选择了b+树的原因。

MySQL的索引结构 B+树和 hash 有什么区别?

哈希索引适合等值查询,但是无法进行范围查询 和模糊查询
哈希索引没办法利用索引完成排序
哈希索引不支持多列联合索引的最左匹配规则
如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题  

有like关键字的时候,如何判断走不走索引?

我们在判断一个查询走不走索引的关键原因,还是要看它的索引结构是否支持这种查询。

一般来说,我们在进行查询的时候,like的后面跟的值的%是前置的,那么就不会走索引,如果后面跟的百分号是后置的,那么会走索引。

为什么呢?因为我们mysql的索引,是从左到右一个索引字段一个索引字段的排序的,也就是说,当我们的%是前置的时候,那么意味着全匹配,对于mysql来说如果走索引那就是要查询所有的索引字段,而且查完之后还要回表,没有意义,不如全表查询。

但是这种情况也不是完全如此,当我们在查询的时候,查询的字段如果是主键id,或者查询的列刚好是索引字段,那么mysql基于成本计算,也是会走索引的,因为它可以直接在索引中直接找到所有需要的字段,而不需要回表。

但是如果查询的字段里面,有字段不是索引,那么对于mysql来说,因为要查询这个不是索引的字段,反正还是要进行全表查询的,那就没有必要去查询索引了,直接全表查询就好了。

讲一下mysql的事务?

什么是mysql的事务呢?

首先我们知道事务,无非就是ACID,即原子性,隔离性,一致性,持久性。

那么数据库mysql中的事务,就是把需要保证原子性,隔离性,一致性,持久性的一个或者多个数据库操作,称之为数据库的事务。

原子性,就是一个事务中的所有操作要保证同时成功,或者同时失败。

隔离性,就是让不同事务的两个操作,都不会互相影响。

持久性,就是让事务最终执行后的结果,要持久化到磁盘中,不管后续如何变化,本次事务的结果都不会改变。

一致性,重点讲一下一致性,一致性,我觉得可以简单的理解一下,就是原子性,隔离性和持久性,这三者的目的,最终都是为了保证一致性。

事务里面的一致性,指的就是这个事务的一系列操作,是符合现实世界的规则和约束的。这里要注意的是,因为关系型数据库,本身就是现实世界的一个映射,因此一致性,就是符合现实世界的规则和约束,也是合理的。

一个简单的比方,A向B转账,A要减去一笔钱,B要增加一笔钱。这就是现实世界的逻辑,要在数据库中得到实现的话,就是数据库的事务一致性。

而原子性,隔离性和持久性,目的都是为了维护这种现实世界对数据库的要求规则和约束而存在的。

当前,在现实的编码环境中,数据库事务的一致性,更大程度上还是需要程序员们在业务代码上自己控制,数据库本身也只是支持了一部分而已。

再讲一下事务,我们知道,事务其实是一个抽象的概念,它对应着一个或者多个数据库操作,一般来说,事务有如下几种状态:

  1. 活动的,这种状态指的是事务正处于一个运行状态。
  2. 部分提交的,这是指代事务操作已经执行完了,但是最后一步将结果刷新到磁盘中还没有做。
  3. 失败的,事务在执行过程中,遇到了一些错误,而导致事务停止执行了。
  4. 中止的,当事务失败之后,需要回滚。当回滚执行完毕后,事务就是处于中止状态。
  5. 提交的,当事务的执行结果刷新到磁盘后,事务的状态就是提交的。

这大概就是数据库的一个事务了。

讲一下mysql的事务隔离级别都有哪些?

MySQL的隔离级别主要有4种,未提交读,已提交读,可重复读,可串行化。

这四种隔离级别,主要的不同还是他们对于一些关于一致性的异常情况的防范级别不同。我们都知道,数据库的一致性有四种异常,脏写,脏读,不可重复读,幻读。

Mysql的四种事务隔离级别,都解决了脏写情况.

但是对于剩下的三种异常,未提交读这种事务分离级别,脏读,不可重复读,幻读,这三种现象都有可能发生。

已提交读则是不可能发生脏读情况,但是可能发生不可重复读和幻读现象。

可重复读,则是不可能发生脏读,脏写和不可重复读现象,但是会发生幻读现象。

那么可串行化,则是四种现象都不可能发生。

但是我们要知道,有的就有失,一般来说,未提交读的速度是最快的,但是风险是最高的,可串行化则是读取速度最慢的,但是也是最安全的。

所以,在效率和安全的取舍上,Mysql默认的是可重复读。

 

为了一致性读(快照读),或者说了为了解决幻读的情况,mysql采用了什么办法?

  什么是一致性读呢?在我们的mysql中,如果同时有两个线程对同一条数据进行操作,一条数据进行读取,那么这条读取的线程,查询到的符合条件的数据是4条,它的事务还没有提交的时候,而这个时候另外一个事务在这之后插入了1条符合第一个事务的数据,然后第一个事务第二次进行读取,发现读取到的数据居然变成了5条,这就出现一致性读的问题。

  这种问题的出现,是经典的幻读现象,实际上mysql默认的事务隔离级别Reapetable Read中,它是可以去除掉脏写,脏读,不可重复读这三种现象的,但是对于幻读现象,是有可能出现的,而幻读现象,就是造成了我刚刚讲述的一致性读的问题。

  那么mysql为了解决这个问题,就提出了MVCC方案。

  那么什么是MVCC方案呢?

  直观的理解,MVCC方案就是我们所说的版本链了。那么mysql是如何实现这个版本链的呢?首先,在我们的聚簇索引记录中,每一条行记录都保存了两个参数,一个事务id,一个旧版本的指针,这个指针可以找到我们这条数据的上一次undo日志。

  每对一条记录进行改动,mysql都会记录一条undo日志,每一条undo日志也会和聚簇索引记录中的数据一样,有一个旧版本的指针和它所属的事务的事务id,这样子,聚簇索引和undo日志会形成一个链表结构,这个链表结构的头节点,就是我们这个列的最新值。

  当一个事务来读取数据的时候,mysql在生成一个事务id给这个事务的同时,会生成一个readView一致性视图,要注意的是,在可重复读的事务隔离级别下,只有这个事务在第一次读取的时候,才会生成readView一致性视图,后续读取就还是使用第一次读取的时候生成的readView一致性视图,在这个事务来查询数据库的时候,我们会通过这个事务的事务id,获取到属于这个事务的版本是在这个链表的哪一个,然后返回这个值给到这个事务。

还有就是 间隙锁(Gap Lock)与临键锁(Next-Key Lock)

  • 适用场景‌:加锁操作(如SELECT ... FOR UPDATEUPDATEDELETE)78

  • 锁机制‌:

    锁类型作用范围防幻读原理
    间隙锁索引记录间的间隙阻止在锁定间隙内插入新数据(如id BETWEEN 10 AND 20)810
    临键锁记录锁 + 间隙锁(锁定左开右闭区间)同时锁住记录和相邻间隙(如锁定(5,10]范围)10
    • 示例‌:SELECT * FROM t WHERE d=5 FOR UPDATE会锁住d=5相关的所有间隙2

mysql都有哪些锁?

在数据库的操作中,有三种类型的操作,读-读操作,读-写操作,写-写操作。

首先我们可以确定的读-读操作中,我们可以确定,是不需要通过加锁的形式来保证数据的一致性的。

那么在读-写操作中,如何通过锁的形式来保证数据的一致性,那么性能就会很受限,所以一般都是通过MVCC版本控制的形式来保证数据的一致性操作。

唯独在写-写操作中,我们必须通过锁的形式,来进行数据库写-写操作的一个排队。

首先我们要知道什么是锁?

其实锁,本质上来讲就是一个内存中的结构,比如说我们的行级锁,每一条数据,都会在数据中保存一个锁结构,这个锁结构有两个比较重要的数据:

  1. trx信息:表示这个锁是和哪个事务关联的。
  2. is_waiting:表示当前这个锁的状态,是正在等待,还是已经启动。

那么,这就是锁的真实面目。

那么,从锁的粒度上来讲,有三个级别的锁:

  1. 行级锁,这个锁是锁到了每一条数据。
  2. 页级锁,这个锁是锁到了每一个数据页。数据页,在B+树中的一个节点,就是一个数据页。
  3. 表级锁,这个锁是锁到了整个数据库表的。

从锁的功能上来讲,有两个类型的锁:

  1. 共享锁,Shared Lock,简称为S锁。这种锁,主要还是针对读操作,在事务要读取一条记录的时候,需要先获取到该记录的S锁。
  2. 独占锁,Exclusive Lock,简称为X锁。这种锁,主要还是针对写操作,在事务要改动一条记录的时候,需要获取到这条记录的X锁。

如果一个事务A获取到一条记录的共享S锁,那么另一个事务B也可以获取到这个记录的共享S锁,但是不能获取到这个记录的独占X锁。

如果一个事务A获取到一条记录的独占X锁,那么另一个事物B既不能获取到这个记录的共享S锁,也不能获取到这个记录的独占X锁。

mysql什么时候会出现死锁?如何解决?

mysql中出现死锁的情况,基本上是因为如下原因:

首先是 第一种情况

A事务获取到了数据data1的X锁,B事务获取到了数据data2的X锁,然后A事务又要去获取data2的X锁或者S锁,但是获取不到,只好等待。而B事务也要去获取data1的X锁获取S锁,也获取不到,只好等待。

这个时候,A事务和B事务都因为获取不到锁而陷入到等待中,这种情况就是死锁。

一般来说,在发生死锁后,InnoDb引擎会回滚一个事务,来释放掉该事务所获取的锁。

而在开发层面,我们需要找到哪些发生死锁的语句,通过优化语句来改变加锁顺序,或者建立合适的索引来改变加锁顺序,避免死锁情况的发生。

比如说刚刚的那个例子,可以让A事务和B事务,都同时要先获取到数据data1的X锁,再去获取到data2的X锁,这样就可以避免死锁的情况发生。

然后还有 第二种情况,在实际开发中更容易发生,而且更为隐蔽。

A事务,首先是查询data1这条数据,这个时候,mysql分配的锁是共享S锁,但是A事务查询完之后,又想去更新data1这条数据,那么mysql的锁就会升级为独占X锁。

但是就在A事务想去更新data1的数据的时候,也就是A事务的数据库锁即将要从S锁升级为X锁的时候,其实有一个事务B,在A事务想要更新data1之前正想更新data1的数据,企图获取到独占X锁。

而mysql在同一个行记录的时候,分配X锁的机制是顺序性的,因为B先申请独占X锁,那么就应该先给B事务分配独占X锁。

但是又由于A事务先获取了共享X锁,然后没有释放X锁,还想升级到独占X锁,那么B事务就无法拿到这条行记录的独占X锁,B事务只能陷入到等待中,而A事务由于mysql要首先给B事务分配X锁,而无法升级到X锁,那么A事务也只能陷入到等待中。

那么A事务和B事务都陷入到了等待中,这就是一个死锁。

解决这种死锁的问题,一般都是通过乐观锁的手段解决,在数据库表中加一个version字段,每一次更新的时候,将version+1,且在where字句判断version的值是没有变化的,如果有变化,那就代表数据过期了,更新失败。

当然也可以在A事务查询的时候,直接升级为X锁,方法就是在select语句之后加一个for update语句,锁住这条记录,这样其他事务来修改的时候因为拿不到X锁,就直接陷入等待,要到A事务释放掉X锁,其他事务才能进行修改。

另外要注意的是,如果数据库表的数量很大,但是索引又没有,在查询的过程中,很容易导致出现全表扫描的情况,但是全表扫描,因为是将锁的粒度升级到了表级锁,那么就会很容易出现死锁的情况。

Myisam引擎和innodb引擎的区别?

首先,两者的索引方案是不一样的。Innodb的所有是分为聚簇索引和非聚簇索引,聚簇索引是主键id当非叶子节点的值,但是在叶子节点,除了主键id,还会将数据表其他字段也存入其中,而非聚簇索引就是不管是叶子节点还是非叶子节点,存储都是索引列。Mysaim的索引主要的不同是mysaim是没有聚簇索引的,它有主键索引,但是主键索引上的叶子节点是不会存储数据库表的数据,而是将数据表的所有数据单独存储到一个文件,说白了,就是索引和数据是分开的,mysaim存储的索引都是二级索引,包括主键索引,都需要进行二次回表。

另外,myisam是不支持事务的,innodb是支持事务的,而且mysaim是只有表级锁,而innodb除了表级锁,还有行级锁。

 

mysql聚簇索引和非聚簇索引有什么区别?

聚簇索引,就是我们常说的主键索引,也是b+树构成的,它是用主键值的大小进行记录和数据页的排序,另外,b+树的叶子节点存储的是完整的表数据,而非叶子节点存储就是主键id。

非聚簇索引,就是我们所说的二级索引,联合索引等等。这些索引的特点是,使用索引列的大小进行表记录和页的排序,然后b+树的叶子节点存储不再是完整的表数据,而是索引列+主键id,而非叶子节点则是存储的索引列和下一层级节点数据页的页码。

 

索引下推指的是啥?

比如说我们有一个联合索引,索引列是A和B,那么我们在进行查询的时候,查询条件是关于A的查询条件和关于B的查询条件。在没有索引下推的时候,我们先查询A的查询条件,发现是可以通过索引查询的,但是B的查询条件是不能通过索引查询的,典型的就是b的查询是like,而且%是前置的。

那么,理论上来讲,当我们在进行查询的时候,应该是通过索引列A查询以后,发现不能通过索引列B查询,那么我们应该是在索引中查询到符合条件A的数据,然后回表后再去判断是否符合条件B。

而索引条件下推,就是说,当查询到符合条件A的数据的时候,先不回表,而是直接去通过索引B判断这条数据是否符合查询条件,如果符合,再去回表查询所有的数据。如果不符合,跳到下一条数据去。

这样子可以极大的减少因为回表带来的IO性能损耗。

如何对慢sql语句进行优化?

如果我们碰到了慢sql,需要对慢sql进行优化的时候,我们需要一个整体上的梳理和了解,我们才能找到一个正确的方向去优化。

首先,我们肯定是要对sql进行优化的。

既然是慢sql,那么我们要看看这个sql是否走了索引。通过explain来查看执行计划,如果发现没有走索引,那么我们就需要对sql来进行优化,或者是在表里面加索引。

如果where字句后面的查询条件是有索引的,但是又没有走索引,那么我们可以看看是否是因为在查询条件中,有一些东西导致了没有走索引。

比如说sql中用了函数,这种情况下一般就不会走索引,或者是用了like语句,或者select* 的操作等等。

如果sql优化完了,发现这个用了索引,性能得到了一定的提升,但是又提升的不是很多。我们可以看看数据库表中的总的数据有多少条,如果超过了500w条数据,那么我们考虑可以使用分库分表的形式,来提升我们数据库的读写性能。

但是这种数据库的分库分表,其实是逼不得已,且业务是个重点业务的情况下,才会做的事情,如果确定这个业务是个重点查询业务,但是写的情况不是很多,其实可以考虑使用redis来缓存这个数据,通过缓存的形式,提升整个程序的读的能力。

mysql并发执行数据更新会出现脏写?

MySQL并发情况下更新数据,正常应该是不会新增脏数据。但不排除一种情况,那就是在程序逻辑是判断如果存在则更新不存在则新增数据。这种情况下如果没有唯一索引的约束,就会产生脏数据。

这种情况其实和并发情况下事务产生脏读类似。

并发情况下如果事务的隔离级别过低(未提交读);则有可能会出现脏读的情况,也就是一个事务读到了另一个事务没有提交的更新数据。也有可能撤销事务时把另一个事务的更新结果覆盖,也就是丢失更新。

sql的索引什么时候下会失效?

导致 MySQL 索引失效的常见场景有以下 6 种:

  1. 联合索引不满足最左匹配原则。
  2. 模糊查询最前面的为不确定匹配字符。
  3. 索引列参与了运算。
  4. 索引列使用了函数。
  5. 索引列存在类型转换。
  6. 索引列使用 is not null 查询。

redoLog和undoLog的区别

undo log 和 redo log 是 MySQL InnoDB 存储引擎实现事务和崩溃恢复的核心日志,它们在定义、作用、类型和写入时机上存在显著区别:

一、核心区别

特性Undo Log (回滚日志)Redo Log (重做日志)
本质逻辑日志(记录行级操作前的旧值或反向操作逻辑)48物理日志(记录数据页的物理修改)16
主要作用1. 事务回滚(撤销未提交的修改) 2. 实现 MVCC(多版本并发控制)481. 崩溃恢复(重做已提交的修改) 2. 提高写性能(顺序写替代随机写)67
目标保障事务的原子性(Atomicity)和一致性(Consistency)47保障事务的持久性(Durability)17
存储内容- INSERT:记录删除逻辑 - DELETE:记录插入逻辑 - UPDATE:记录旧值4数据页的物理修改(如页号、偏移量、修改后的值)16

二、写入时机

1. Undo Log 写入时机

  • 事务修改数据前写入‌:
    在执行 INSERT/UPDATE/DELETE 操作前,先将修改前的数据(或反向操作逻辑)写入 undo log 并‌持久化到磁盘‌58。
  • 为什么先写?
    确保事务回滚时可追溯旧值(若未持久化,崩溃后无法回滚)48。
  • 其他作用‌:
    为其他事务提供 MVCC 所需的旧版本数据快照411。

2. Redo Log 写入时机

  • 事务执行中写入内存‌:
    修改数据页时,先将变更记录写入 ‌redo log buffer(内存缓冲区) ‌56。

  • 事务提交时刷盘‌:
    根据 innodb_flush_log_at_trx_commit 参数决定刷盘策略:

    • 1(默认) ‌:事务提交时同步刷盘(保证崩溃后不丢失)56。
    • 0‌:每秒刷盘(可能丢失最近 1 秒的数据)6。
    • 2‌:写 OS 缓存,不强制刷盘(依赖 OS 刷新)6。
  • 两阶段提交(涉及 Binlog) ‌:
    在事务提交时,redo log 先标记为 prepare 状态,待 binlog 写入完成后标记为 commit59。

三、关键总结

日志类型写入阶段持久化要求目的
Undo Log事务修改数据‌‌立即写入强制刷盘(确保回滚能力)58原子性、MVCC
Redo Log事务修改数据时写 buffer 提交时按策略刷盘按策略刷盘(确保持久性)56崩溃恢复、提高写性能

四、协作关系

  • Undo log ‌不是‌ redo log 的逆向操作,两者协同保障事务特性:

    • Undo log 用于回滚未提交的事务37;
    • Redo log 用于重做已提交的事务37。
  • 例如:事务回滚时,先用 undo log 恢复数据,若此时崩溃,重启后 redo log 会重做已提交的其他事务,但不会重做本事务(因未提交)512。