怎么比较两个索引的好坏?

典型回答 我们都知道,想要看一个 SQL 有没有用到索引,可以通过执行计划来看,并且 MySQL 的索引选择是优化器根据成本预估进行的。也就是说,其实是有可能优化器最终选错索引的。 那么,我们自己在做分析的时候,如何去评判一条 SQL 用到的索引是好是坏呢?如果一个 SQL 可以用到多个索引,那到底应该用哪个更合适呢? ✅为什么MySQL会选错索引,如何解决? 根据上面这篇文章中,我们可以知道,优化器评判一个索引的好坏的依据就是成本,成本低的索引就是好的,成本高的索引就是不好的。 假如说有以下 SQL: 1 EXPLAIN SELECT * FROM orders WHERE customer_name = "Hollis" and age = 12; 在不考虑索引合并的情况下,他可能会用到customer_name的索引,也可能会用到age的索引。那么,当我们分析两个索引的好坏的时候,有两种非常直观的方式: 1、直接运行一下 SQL,看看他的执行时长 2、通过执行计划分析 运行时长 第一种方式最直接,就是使用指定的索引直接运行一下 SQL,看看他的整体耗时就行了。如: 1 SELECT * FROM orders FORCE INDEX (customer_name) WHERE customer_name = 'Hollis' AND age = 12; 1 SELECT * FROM orders FORCE INDEX (age) WHERE customer_name = 'Hollis' AND age = 12; 这里使用 force index进行指定索引。如果是 MySQL 8.0之前,可以用SHOW PROFILES、如果是 MySQL 8.0之后的版本,可以用EXPLAIN ANALYZE来查看 SQL 的耗时情况。具体参考: ...

March 22, 2026 · 1 min · santu

MySQL 为什么是小表驱动大表,为什么能提高查询性能?

典型回答 ✅MySQL的驱动表是什么?MySQL怎么选的? 我们介绍过,当进行联接查询时,MySQL 通常会选择较小的表作为驱动表,然后在较大的表中查找匹配的记录。这种策略的核心思想是减少扫描和比较的次数,从而提高查询性能。 假设我们有两个表:employees(1000 条记录)和 departments(10 条记录),并且要进行以下查询: 1 2 3 SELECT e.name, d.department_name FROM employees e JOIN departments d ON e.department_id = d.id 在不考虑hash join等其他链接方式,只考虑nested loop join的情况下,其实执行的次数是笛卡尔积,即: 1 2 3 4 5 6 7 8 9 for(1000){ for(10) } 和 for(10){ for(1000) } 但是,假设employees.department_id和departments.id 都有索引的情况下,就不一样了,因为索引的查询是比较快的,他的复杂度是log(n)。那么: 大表驱动小表,复杂度为:O(1000) * O(log 10) 小表驱动大表,复杂度为:O(10) * O(log 1000) 这样一算的话,就非常清楚了,肯定是小表驱动大表的整体的复杂度更低!

March 22, 2026 · 1 min · santu

什么是数据库的锁升级,Innodb 支持吗?

典型回答 锁升级是指数据库系统将多个较小的锁(如行锁)合并为一个较大的锁(如表锁)的过程,以减少锁的管理开销。 但是 InnoDB是不支持锁升级的!默认使用行级锁进行并发控制。 ✅介绍下InnoDB的锁机制? InnoDB 设计的目标是高并发和高性能。自动锁升级虽然可以减少锁的管理开销,但会降低并发性能,因为较大的锁(如表锁)会导致更多的事务等待锁释放,从而增加锁冲突和事务延迟。

March 22, 2026 · 1 min · santu

数据库扫表任务如何避免出现死循环

典型回答 这个问题听上去有点奇怪,一般面试也不会这么问,但是这个问题我见过很多次,甚至很多我大厂的同事写代码也会这么写。 最近一次就发生在上周,我们做 CR 的时候,一个 P7级别的同事写的代码也出现了这个问题,所以单独拿出来讲讲。帮大家避坑。 假如我们有一张表case_event,其中有一个字段state,它有三个值,分别是INIT、SUCCESS、以及 FAILED。 那么在定时任务中,我们需要把 INIT 的数据扫描出来进行执行,一般来说是这么写的 SQL: 1 2 3 4 SELECT * FROM case_event WHERE STATE = 'INIT' ORDER BY ID LIMIT 200; 这个 SQL 看上去没啥问题,其实就是每次扫描200条记录处理。 但是这个SQL其实是一个典型的 bad case,因为他会出现一个致命的问题,那就是可能会导致扫描任务一直无法执行。 因为上述的 SQL 相当于默认了每一条记录执行之后,都能把状态推进到 SUCCESS 或者 FAILED。但是事实上并不一定的,尤其是在一些有很复杂的业务逻辑,或者一些外部调用的时候,这个地方就变成了一个分布式事务,我们没办法保证最后的 INIT->SUCCESS 或者 INIT->FAILED 一定能成功。 那如果不能成功,就会导致一部分失败的状态一直处于 INIT 状态,那么他就会每次都会被扫描起来(因为他还在前200条之内),然后还是不成功,下次还会被扫描出来。 这样一方面会大大降低任务的效率,一直在重复执行这些不断失败的任务,另一方面,一旦失败的条数达到了200条,那么就意味着每次扫出来的数据都是这200条,导致后面的任务永远无法被执行到。 而如果你的SQL 是这么写的,那么这个问题就更大了: 1 2 3 4 SELECT * FROM case_event WHERE STATE in ('INIT' ,'FAILED') ORDER BY ID LIMIT 200; 相当于你在不断的重复执行那些固定的任务,而后面的很多任务一直无法被执行。 ...

March 22, 2026 · 1 min · santu

A,B,C的联合索引,按照 AB,AC,BC查询,能走索引吗?

典型回答 在不考虑 MySQL 8.0中的索引跳跃扫描(先别管是啥,后文有介绍)的情况下,走索引情况如下: 查询条件 是否走索引 A 能走 B 不走 C 不走 AB 能走 AC 能走 BC 不走 ABC 能走 BA 同 AB CA 同 AC CB 同 BC BAC 同 ABC CBA 同 ABC 通过以上表格,你会发现,只有条件中包含 A 的才能走索引,不包含A 的都不能走索引。原因就是因为最左前缀匹配。 ✅什么是最左前缀匹配?为什么要遵守? A,B,C 三个字段创建的联合索引,A 字段在最左边,其次是 B,在其次才是 C,所以,按照最左前缀匹配的原则,想要走索引,至少要先 match 上最左边的 A。 在命中了 A 的情况下,AB 相比于 AC 的性能要更好一些,因为他能用到 A 和 B两个字段的索引,而 AC 只能用上 A,而用不上 C,因为他跳过了 B。 然后,还有就是 AB 和BA 是一样的,where 条件中的先后顺序,不影响索引的使用。 ✅where条件的顺序影响使用索引吗? 扩展知识 ✅什么是索引跳跃扫描 ...

March 22, 2026 · 1 min · santu

MySQL是AP的还是CP的系统?

典型回答 CAP理论,是描述分布式系统的,即在一个分布式系统中,可用性、一致性和分区容错性不能同时满足。 ✅什么是CAP理论,为什么不能同时满足? 但是,我们日常使用的MySQL,虽然也是有多个节点的,但是大多数情况下都是主备的,即只有一个主节点对外提供服务,当主节点挂了之后,备节点顶上来。 也就是说,同一个时刻,只有一个节点对外提供服务的,这就谈不上P了,没啥分区容错性可言。 而在可用性(A)及一致性(C)方面,因为只有一个节点对外提供服务,所以自然可用性和一致性都是可以保障的。(即使发生了主从延迟,也不影响主节点的一致性或者可用性) 因此,我们可以认为,MySQL 是一个 AC 的数据库。即从节点的延迟不会影响一致性或可用性。 但是,MySQL在使用的时候,还有一种做法,那就是读写分离,即在主库上操作写,在从读库上进行读,这时候主库和从库都是对外提供服务的。这时候的MySQL就从一个AC的系统,变成了一个AP的系统了。 因为在出现网络延迟的时候,MySQL的从节点一样可以正常响应请求,只不过返回的数据可能是旧数据,这就是典型的优先保证了可用性,而牺牲了一致性。 所以,总结一下就是,MySQL在默认情况下,是一个AC的数据库系统,在读写分离的情况下,是一个AP的数据库系统。

March 22, 2026 · 1 min · santu

MySQL的BLOB和TEXT有什么区别_

典型回答 在 MySQL 中,BLOB 和 TEXT 数据类型都用于存储大量数据。BLOB的全称是Binary Large Object,所以他主要被设计出来是存储二进制数据的,而TEXT主要是用于存储文本数据。 所以,当我们要在数据库中存储二进制数据,比如图像、音频、视频等等,就可以把他们的二进制的对象存储到BLOB中。而我们如要存长文本,如文章、扩展信息等等,则通常使用TEXT。 因为BLOB 数据类型按二进制方式处理数据,所以他不会对存储的数据进行字符集转换,并且不支持排序。而TEXT 数据类型则是以字符的方式存储文本数据,并且可以对数据进行字符集转换和排序等操作。 BLOB 和 TEXT 类型都有不同的变种,分别支持不同的存储大小: TINYBLOB / TINYTEXT: 存储最大长度为 255 字节 BLOB / TEXT: 存储最大长度为 65,535 字节(64 KB) MEDIUMBLOB / MEDIUMTEXT: 存储最大长度为 16,777,215 字节(16 MB) LONGBLOB / LONGTEXT: 存储最大长度为 4,294,967,295 字节(4 GB) 存储内容 字符集转换 排序 类型 BLOB 二进制(如视频、音频) 不支持 不支持 TINYBLOB、BLOB、MEDIUMBLOB、LONGBLOB TEXT 文本(如文章内容) 支持 支持 TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT

March 22, 2026 · 1 min · santu

什么是MySQL的内存碎片?如何清理?

典型回答 在MySQL中,碎片主要指的是由于数据的插入、更新和删除操作导致的存储空间不连续和浪费。 碎片会导致降低查询效率、浪费磁盘空间以及使得备份和恢复的过程变得更慢等问题。 ✅MySQL为什么会有存储(内存)碎片?有什么危害? 所以,我们需要针对碎片进行处理。而处理的办法就是删除这些碎片。要删除碎片,可以考虑以下几种方法: OPTIMIZE TABLE: 使用 OPTIMIZE TABLE 命令可以重新组织表的存储,释放未使用的空间并减少碎片。 1 OPTIMIZE TABLE table_name; 但是,**一般不建议频繁执行 ****OPTIMIZE TABLE** ,而是可以在业务低峰期,比如凌晨进行,主要是因为OPTIMIZE TABLE会锁表。 对于 MyISAM 表,OPTIMIZE TABLE 会锁定表,导致其他操作(如插入、更新、删除)在优化期间被阻塞。 对于 InnoDB 表,MySQL 会对表进行共享锁定,这可能会导致数据写操作被阻塞,影响系统的响应时间。 还有就是,大表上的 OPTIMIZE TABLE 操作可能需要较长时间。时间取决于表的大小和系统资源(如CPU、磁盘IO)的情况。如果表非常大(数百万行或更多),建议在系统负载较低的时候执行。 OPTIMIZE TABLE还会优化过程会重建表的索引,如果索引较多,也会增加操作时间。 ALTER TABLE: 通过 ALTER TABLE 语句,可以重建表以清理碎片。 1 ALTER TABLE table_name ENGINE=InnoDB; 扩展知识 是否需要经常清理碎片? 是否需要及时处理碎片取决于以下情况: 性能影响: 如果你的查询性能明显下降,碎片可能是其中一个原因。在这种情况下,清理碎片有助于提高查询速度。 存储空间问题: 如果表的碎片过多,会导致磁盘空间浪费。此时清理碎片可以释放存储空间。 数据变更频繁: 对于频繁进行大量插入、更新和删除操作的表,定期优化有助于保持表的性能。 碎片量较小: 如果表中的碎片较少,对性能的影响不大,可以不频繁进行优化。碎片不一定需要每次都及时处理。 总的来说,只有当碎片影响到性能时,才需要进行 OPTIMIZE TABLE 等操作。 如何查看碎片情况 可以通过show table status查看表相关信息,其中包括碎片信息。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 SHOW TABLE STATUS LIKE 'hollis_table'; *************************** 1. row *************************** Name: salaries Engine: InnoDB Version: 10 Row_format: Dynamic Rows: 2312918 Avg_row_length: 31 Data_length: 90421296 Max_data_length: 0 Index_length: 0 Data_free: 412304 Auto_increment: NULL Create_time: 2025-07-01 22:33:47 Update_time: 2025-07-01 22:34:42 Check_time: NULL Collation: utf8_bin Checksum: NULL Create_options: Comment: 1 row in set (0.00 sec) 这里面的Data_free字段,表示未使用的空间(单位:字节)。通常表示表中的碎片空间。Data_free 值较大的表通常意味着碎片较多,需要优化。 ...

March 22, 2026 · 1 min · santu

MySQL做索引更新的时候,会锁表吗?

典型回答 看版本,在MySQL 5.6版本中,已经支持了Online DDL,一般来说是不会锁表了。 ✅什么是OnlineDDL 但是,虽然支持了Online DDL也不建议在业务高峰期做变更。因为Online DDL是尽最大可能保证DDL期间不阻塞DML动作。但是需要注意,这里说的尽最大可能意味着不是所有DDL语句都会使用OnlineDDL加速。 资源争抢:而且,**Online DDL 在执行过程中会占用系统资源,如 CPU、内存和 I/O。**这可能会对数据库的性能产生一定影响,尤其是在数据量较大的情况下。在非高峰期执行 DDL 变更可以减少对业务操作的干扰,避免高峰期因为资源争用而影响系统性能。 也可能会锁表:虽然 Online DDL 尽可能得减少了锁表的时间,**但在某些复杂操作(如涉及索引重建、大量数据的表结构更改)中,可能仍会有短暂的锁表情况。**所以在非高峰期进行变更,可以降低长时间锁等待对用户请求的影响。 主从延迟:在主从复制的数据库架构中,DDL 操作需要同步到从库。如果高峰期有大量写入操作,DDL 操作可能会增加主从同步延迟。

March 22, 2026 · 1 min · santu

MySQL如何实现行转列和列转行?

典型回答 行转列 所谓行转列,就是假设有如下数据表 sales: id year product sales 1 2020 A 100 2 2020 B 200 3 2021 A 150 4 2021 B 300 要将 product 列的不同值(A 和 B)变为列标题,year 作为行标题,生成如下结果: year A B 2020 100 200 2021 150 300 这里,可以使用CASE WHEN THEN 用来根据条件为每个 product 创建一列,并通过 SUM 聚合同一年份的销售数据,达到行转列的效果。 1 2 3 4 5 6 SELECT year, SUM(CASE WHEN product = 'A' THEN sales ELSE 0 END) AS A, SUM(CASE WHEN product = 'B' THEN sales ELSE 0 END) AS B FROM sales GROUP BY year; 简答解释下: ...

March 22, 2026 · 2 min · santu

留言给博主