今年环境越来越难了, 准备了一些面试题分享一下,持续更新中!着急的可看最后,
话不多说,五一消费节,先搞个3块钱,能省则省,再继续看文吧
1. 说说mysql的优点
- 数据存储:可以高效地存储大量的数据,并支持多种数据类型。
- 性能:通过优化查询和索引等机制,提供较好的性能表现。
- 可靠性:具备数据备份、恢复等功能,保证数据的安全性和可靠性。
- 易用性:操作相对简单,有丰富的管理工具和接口。
- 扩展性:可以通过集群等方式进行扩展,以满足不断增长的业务需求。
2. MyISAM索引与InnoDB索引的区别?
- 存储方式:MyISAM使用非聚簇索引,索引文件和数据文件是分开的;而InnoDB使用聚簇索引,将索引和数据一起存储在同一个文件中。
- 锁机制:MyISAM采用表级锁定,意味着当对表进行写操作时,整个表都会被锁定,因此可能导致并发写操作的性能较差。而InnoDB采用行级锁定,只锁定需要修改的行,可以提供更好的并发性能和多用户写入的支持。
- 事务支持:MyISAM不支持事务处理,而InnoDB支持事务和ACID特性(原子性、一致性、隔离性和持久性),可以进行事务管理、回滚和恢复操作。
- 引用完整性:MyISAM不支持外键约束,而InnoDB支持外键约束,可以设置关联关系来保证数据的完整性。
- 性能特点:MyISAM在读取频繁、插入和更新较少的场景下性能较好,特别适合于读密集型应用;而InnoDB在并发写入和更新较多的情况下性能较好,适合于写入密集型应用或需要事务支持的场景。
3. MySQL为什么默认引擎是InnoDB
- 支持事务:InnoDB 是一个支持事务的存储引擎。事务是一组数据库操作的原子性执行,可以保证操作的一致性和完整性。
- 并发控制:InnoDB 支持行级锁定,在高并发环境下可以最大程度地减少锁冲突,提高并发性能。相比之下,MySQL 的另一个存储引擎 MyISAM 只支持表级锁定,并发性能较低。
- 外键约束:InnoDB 支持外键约束,可以保证数据的完整性。外键用于建立表与表之间的连接,通过外键约束可以实现数据之间的关联和参照完整性。
- 崩溃恢复:InnoDB 具有自动崩溃恢复的能力。即使在发生意外故障或系统崩溃时,InnoDB 引擎也能够自动进行崩溃恢复,保障数据的一致性。
- 支持热备份:InnoDB 支持在线热备份,可以在不停止数据库服务的情况下进行备份操作。这对于需要实时运行且对数据可用性要求高的应用程序非常重要。
4. 创建索引时需要注意什么?
- 选择适当的列:根据实际查询需求和数据特点,选择合适的列作为索引列。通常情况下,选择经常被用于查询条件、排序或聚合操作的列作为索引列可以获得更好的性能。
- 避免过多的索引:过多的索引会增加存储空间的开销,并且在数据写入时需要维护多个索引结构,导致性能下降。因此,只创建必要的索引,避免不必要的冗余索引。
- 考虑联合索引:对于多个列的组合查询条件,可以考虑创建复合索引。复合索引可以覆盖多个列,减少查询时需要访问的索引数量,提高查询效率。
- 注意索引的顺序:对于多列索引,考虑索引的顺序。将最常用的列放在索引的前面,以提高查询性能。
- 使用合适的数据类型:选择合适的数据类型可以减小索引的大小,提高索引的效率。尽量避免使用过长或过大的数据类型作为索引列。
5. 什么时候不要使用索引?
在大多数情况下,使用索引可以提高数据库查询的性能和效率。但是有时不使用索引可能更合适,例如:
- 小型表:对于非常小的表,可能不需要使用索引。因为索引的维护需要额外的存储空间和计算资源,而对于小型表来说,全表扫描的开销可能相对较小。
- 插入、更新和删除频繁的表:如果表的插入、更新和删除操作非常频繁,而且对查询性能的要求相对较低,则可以考虑不使用索引。因为索引的维护会增加写操作的开销,可能导致性能下降。
- 查询条件不经常使用:如果某个字段很少用作查询条件,而且表的数据量较小,那么为该字段创建索引可能没有太大的性能提升。
6. B 树和B+树的区别?
- 数据存储方式:在B树中,每个节点都包含键和对应的值,叶子节点存储了实际的数据记录;而B+树中,只有叶子节点存储了实际的数据记录,非叶子节点只包含键信息和子节点的指针。
- 数据检索方式:在B树中,由于非叶子节点也存储了数据,所以查询时可以直接在非叶子节点找到对应的数据,具有更短的查询路径;而B+树的所有数据都存储在叶子节点上,只有通过叶子节点才能获取到完整的数据。
- 范围查询效率:由于B+树的所有数据都存储在叶子节点上,并且叶子节点之间使用链表连接,所以范围查询的效率较高。而在B树中,范围查询需要通过遍历多个层级的节点,效率相对较低。
- 适用场景:B树适合进行随机读写操作,因为每个节点都包含了数据;而B+树适合进行范围查询和顺序访问,因为数据都存储在叶子节点上,并且叶子节点之间使用链表连接,有利于顺序遍历。
总结来说: B树和B+树在数据存储方式、数据检索方式、范围查询效率以及适用场景方面存在区别。B树适合随机读写操作,而B+树适合范围查询和顺序访问
7. MySQL索引底层结构为什么使用 B+树
- 良好的平衡性:B+树是一种自平衡的树结构,不论是在插入、删除还是查询操作中,它都能保持相对较好的平衡状态。这使得B+树能够快速定位到目标数据,提高查询效率。
- 顺序访问性:B+树的所有叶子节点是按照索引键的顺序排序的。这使得范围查询和顺序访问非常高效,因为相邻的数据通常在物理上也是相邻存储的,可以利用磁盘预读提高IO效率。
- 存储效率:B+树在内存中的节点大小通常比其他树结构更大,这样可以减少磁盘I/O操作的次数。同时,B+树的非叶子节点只存储索引列的值,而不包含实际数据,这进一步减小了索引的尺寸。
- 支持高并发:B+树的特性使得它能够支持高并发的读写操作。通过使用合适的锁或事务隔离级别,多个并发查询和更新操作可以同时进行而不会出现严重的阻塞或冲突。
- 易于扩展和维护:B+树的结构相对简单,可以较容易地进行扩展和维护。当插入或删除数据时,B+树只需要调整路径上的少数节点,而不需要整颗树的重构。这样能够有效降低维护成本,并保证索引的高性能。
8. MySQL 中有哪几种锁?
- 表级锁(Table-level Locking):在事务操作中对整个表进行加锁。当一个事务对表进行写入操作时,其他事务无法对该表进行任何读写操作。表级锁通常是针对特定的DDL操作或备份操作。
- 共享锁(Shared Lock):也称为读锁(Read Lock),用于允许多个事务同时读取同一资源,但禁止并发写入操作。其他事务可以获取共享锁,但无法获取排他锁。
- 排他锁(Exclusive Lock):也称为写锁(Write Lock),用于独占地锁定资源,阻止其他事务的读写操作。其他事务无法获取共享锁或排他锁,直到持有排他锁的事务释放锁。
- 行级锁(Row-level Locking):也称为记录锁(Record Locking),在事务操作中对数据行进行加锁。行级锁可以控制并发读写操作,不同事务之间可以并发地访问不同行的数据。MySQL的InnoDB存储引擎默认使用行级锁。
- 记录锁(Record Lock):用于行级锁的一种形式,锁定数据库中的一个记录(行)以保证事务的隔离性和完整性。
- 间隙锁(Gap Lock):用于行级锁的一种形式,锁定两个记录之间的间隙。它可以防止其他事务在该间隙中插入新记录,从而保证数据的一致性。
- 临键锁(Next-Key Locks): 临键锁是记录锁和间隙锁的结合,锁定的是一个范围,并且包括记录本身。
MyISAM存储引擎使用表级锁来控制并发访问,而InnoDB存储引擎则支持更细粒度的行级锁
9. mysql 中in 和exists 的区别
- IN关键字:使用 IN 条件时,我们提供一个固定的值列表,然后将其与指定列的值进行比较。如果列中的值与列表中的任何一个值匹配,就会返回结果。IN 条件适合用于确定某个字段的值是否在给定的值列表中。
例如,SELECT * FROM table_name WHERE column_name IN (value1, value2, value3);
如果 column_name 的值与 value1、value2 或 value3 中的任何一个相匹配,那么这条记录将会被返回。
- EXISTS关键字:使用 EXISTS 条件时,我们需要指定一个子查询。查询的结果并不重要,而是判断子查询是否返回了至少一行结果。如果子查询返回了结果,EXISTS 条件就会被认为是满足的。EXISTS 条件适合用于判断某个条件是否至少存在于子查询的结果中。
例如,SELECT * FROM table_name WHERE EXISTS (SELECT * FROM another_table WHERE condition);
如果子查询(SELECT * FROM another_table WHERE condition)返回了至少一行结果,那么主查询中的记录将会被返回。
总结来说:
- 使用 IN 条件时,比较的是指定列的值是否在给定的值列表中。
- 使用 EXISTS 条件时,判断的是子查询是否返回了至少一行结果。
10. MySQL多表查询时有哪些连接方式
- 内连接(INNER JOIN):返回同时满足连接条件的行。它通过比较连接列的值,将两个或多个表中匹配的行组合在一起。
- 左外连接(LEFT JOIN):返回左表中的所有行,以及与左表匹配的右表的行。如果右表中没有匹配的行,对应的列将填充为 NULL。
- 右外连接(RIGHT JOIN):返回右表中的所有行,以及与右表匹配的左表的行。如果左表中没有匹配的行,对应的列将填充为 NULL。
- 全外连接(FULL JOIN):返回左右两个表中的所有行。如果某个表中没有匹配的行,对应的列将填充为 NULL。需要注意 MySQL 不支持 FULL JOIN 可以使用UNION ALL 模拟。
- 自连接(Self JOIN):将单个表视为两个独立的表,使用别名来引用同一个表。这种连接适用于在同一个表中根据某些条件关联不同的行。
- 交叉连接(CROSS JOIN):返回两个表的笛卡尔积,即所有可能的组合。它将第一个表的每一行与第二个表的每一行进行组合。
11. 什么是最左前缀原则?
最左前缀原则(Leftmost Prefix Rule)是索引在数据库查询中的一种使用规则。它指的是在使用复合索引时,索引的最左边的连续几个列会被用于查询过滤条件的匹配
当复合索引包含多个列 A、B、C,最左前缀原则要求查询中的过滤条件必须从索引的最左边开始,并且不能跳过中间的列。只有当查询中的过滤条件与索引的最左前缀完全匹配时,索引才能被充分利用
遵循最左前缀原则的好处包括:
- 提高查询性能:通过使用索引的最左前缀,可以最大限度地减少索引扫描的数据量,提高查询的效率和响应时间。
- 减少索引占用空间:在某些情况下,使用最左前缀原则可以减少创建多个索引的需求,节省磁盘空间和索引维护的开销。
12. 什么是覆盖索引?
索引覆盖就是一个SQL在执行时,可以利用索引来快速查找,并且此SQL所要查询的字段在当前索引对应的字段中都包含了,那么就表示此SQL走完索引后不用回表了,所需要的字段都在当前索引的叶子节点上存在,可以直接作为结果返回了
13. 什么是索引下推?
索引下推(Index Condition Pushdown,简称ICP)是一种数据库查询优化技术,它利用了数据库引擎中的索引和过滤条件,将部分过滤工作下推到存储引擎层面进行处理,从而减少不必要的数据读取和传输。
让存储引擎在读取索引页时就进行额外的过滤操作。
联合索引为:(name, age)
执行语句
select * from tuser where name like '张%' and age=10;
在索引查询时,就通过name过滤时就过滤age匹配。
14. 为什么需要数据库连接池呢?
- 提高性能:数据库连接的建立和断开是比较耗时的操作,频繁地创建和销毁连接会增加系统的负担。通过使用连接池,可以避免频繁地创建和关闭连接,减少了连接的开销,提高了系统的性能。
- 资源管理:数据库连接是有限的资源,如果每个请求都创建一个新的连接,可能导致连接过多而耗尽资源。连接池通过对连接的管理和复用,能够更有效地管理数据库连接,避免资源的浪费。
- 并发处理:在高并发的场景下,如果每个请求都去单独连接数据库,可能会导致数据库连接数量过多,从而限制了系统的扩展性。连接池允许多个请求共享连接,减少了数据库连接的数量,提高了并发处理能力。
- 连接可靠性:数据库连接可能会因为网络问题或服务器故障而中断,当发生这种情况时,连接池能够检测到连接的失效,并重新创建一个可用的连接,确保应用程序的可靠运行。
总结来说: 数据库连接池是为了提高性能、更有效地管理资源、提高并发处理能力和增加连接的可靠性而存在的。它通过缓存已经建立好的连接,复用连接以及管理连接的生命周期,从而减少了连接的开销,提高了系统的性能和稳定性。
15. 并发事务带来哪些问题?
- 脏读(Dirty Read):一个事务读取了另一个事务未提交的数据。假设事务A修改了一条数据但未提交,事务B却读取了这个未提交的数据,导致事务B基于不准确的数据做出了错误的决策。
- 不可重复读(Non-repeatable Read):一个事务在多次读取同一数据时,得到了不同的结果。假设事务A读取了一条数据,事务B修改或删除了该数据并提交,然后事务A再次读取同一数据,发现与之前的读取结果不一致,造成数据的不一致性。
- 幻读(Phantom Read):一个事务在多次查询同一范围的数据时,得到了不同数量的结果。假设事务A根据某个条件查询了一组数据,事务B插入了符合该条件的新数据并提交,然后事务A再次查询同一条件下的数据,发现结果集发生了变化,产生了幻觉般的新增数据。
- 丢失修改(Lost Update):两个或多个事务同时修改同一数据,并且最终只有一个事务的修改被保留,其他事务的修改被覆盖或丢失。这种情况可能会导致数据的部分更新丢失,造成数据的不一致性。
16. MySQL 记录货币用什么字段类型
在MySQL中记录货币金额,一般推荐使用DECIMAL字段类型。
DECIMAL字段类型用于存储精确的定点数值,可以指定总共的位数和小数点后的位数。这使得它非常适合用于存储货币金额,因为货币金额通常需要精确到小数点后几位。
17. CHAR 和 VARCHAR 的区别?
- 存储方式:CHAR是固定长度的字符类型,而VARCHAR是可变长度的字符类型。
- 占用空间:CHAR会以固定的长度存储数据,不论实际存储的字符数目,而VARCHAR则根据实际需要的空间动态分配存储。
- 尾随空格:CHAR在存储时会用空格填充到指定长度,并在检索时需要删除尾随空格,而VARCHAR没有这个问题。
- 长度限制:CHAR的长度范围为1到255个字符,而VARCHAR的长度范围也是255个字符,但可以根据需求设定更长的长度。
- 访问效率:由于CHAR是固定长度的,它在某些情况下可能会比VARCHAR稍微快一些。
综上所述,CHAR适合存储长度固定且固定大小的数据,而VARCHAR适用于长度可变的数据。
18. count(1)、count(*) 与 count(列名) 的区别?
- COUNT(1):这种写法中,1表示一个常量值,它会被用于计算查询结果集的行数。由于1是一个常量,在执行COUNT(1)时,数据库不会去访问或读取任何实际的数据,仅仅是对满足条件的行进行计数,因此执行速度相对较快。
- COUNT(*):这种写法中,*表示选取所有列,它会对满足条件的行进行计数。与COUNT(1)不同的是,执行COUNT(*)时,数据库需要读取每一行的数据,然后进行计数操作,因此它可能会比COUNT(1)稍微慢一些。不过,在大多数数据库中,优化器会对COUNT(*)进行特殊处理,可以通过索引等方式进行优化,使得性能相对较好。
- COUNT(列名):这种写法中,列名表示具体的表列,它会对非空(NULL)值的行进行计数。相比于COUNT(1)和COUNT(*),COUNT(列名)会跳过值为NULL的行,只统计非空值的行数。这在某些特定的情况下可能更符合实际需求,例如统计某个列的非空值个数。
总体来说,COUNT(1)和COUNT(*)的性能较好且结果一致,而COUNT(列名)则对非空值进行计数。在实际使用时,可以根据具体的查询需求和性能要求选择适当的写法。
19. UNION 与UNION ALL 的区别?
- UNION:UNION用于合并两个或多个查询结果集,并去除重复的行。它将多个查询的结果合并为一个结果集,并自动去除重复的行。在执行UNION操作时,数据库会进行额外的去重操作,这可能会带来一定的性能开销。
- UNION ALL:UNION ALL同样用于合并查询结果集,但不去除重复的行。它将多个查询的结果简单地合并在一起,包括重复的行。相比于UNION,UNION ALL不进行去重操作,因此执行效率更高。
总结来说:在使用时,可以根据具体的需求来选择合适的操作符。如果需要去除重复的行,可以使用UNION;如果不需要去重,或者对性能要求较高,可以使用UNION ALL
20. 如何快速定位慢SQL
- 启用慢查询日志:在MySQL配置中启用慢查询日志,这样可以记录执行时间超过阈值的查询语句。通过分析慢查询日志,可以找到执行时间较长的SQL语句。
- 使用EXPLAIN分析执行计划:对于慢查询的SQL语句,使用EXPLAIN命令来查看其执行计划。通过分析执行计划,确定查询是否有效利用了索引以及是否存在性能瓶颈。
- 检查索引使用情况:确保查询中涉及的列都有适当的索引,并且查询条件能够充分利用索引。可以使用SHOW INDEX命令或查询表的索引信息来检查索引情况。
- 分析查询语句:仔细分析查询语句本身,检查是否存在冗余的操作、重复的子查询、不必要的排序、大量的JOIN操作等。
21. 慢SQL你是怎么优化的
- 优化查询语句结构:检查是否存在冗余的操作、重复的子查询、不必要的排序、大量的JOIN操作等。优化查询语句的结构和逻辑,减少不必要的数据读取和计算。
- 添加合适的索引:确保查询中涉及的列都有适当的索引,并且查询条件能够充分利用索引。通过使用适当的索引,提高查询的性能。但是要避免过多的索引,因为过多的索引会增加写入操作的开销。
- 使用覆盖索引:如果查询只需要使用索引列的数据而不需要访问表的其他列,可以考虑使用覆盖索引。覆盖索引避免了访问表的额外IO操作,提高查询性能。
- 避免全表扫描:尽量避免全表扫描的情况,通过合适的索引或筛选条件来限制查询范围,减少数据读取量。
- 合理分页查询:对于大数据量的分页查询,可以通过使用LIMIT分页、使用游标、定期同步缓存等方式来提高性能。
22. 索引失效的情况有哪些
- 不满足索引列顺序:如果查询条件中的列顺序与索引列的顺序不一致,索引可能无法被使用。例如,一个联合索引(A, B),如果查询条件只包含了B列而没有A列,那么这个索引就无法被利用。
- 使用函数或表达式:当查询条件中对索引列应用了函数、数学运算、类型转换等操作时,索引可能无法被使用。因为索引的创建是基于原始列值的,无法直接使用函数计算后的结果进行索引匹配。
- 字符串比较问题:对于字符串类型的索引列,使用了不符合索引规则的比较方式。
- 数据类型不匹配:当查询条件的数据类型与索引列的数据类型不匹配时,索引可能无法被使用。尤其是在进行隐式数据类型转换、不同字符集的比较或编码问题时,需要特别留意。
- 数据量过小:当表中的数据量较小时,MySQL可能会选择全表扫描而非使用索引,因为全表扫描的成本较低。这种情况下,索引可能无法发挥作用。
- 使用了NOT、<>、OR等非优化的逻辑操作符:这些逻辑操作符在查询条件中的使用会导致索引失效,因为它们无法充分利用索引的特性。
23. 说下你对数据库事务的理解
原子性、一致性、隔离性,持久性
- 原子性:事务中的所有操作要么全部执行成功,要么全部回滚到事务开始前的状态。如果在事务执行期间发生错误,系统将回滚所有已执行的操作,以保持数据的一致性。
- 一致性:事务的执行不会破坏数据库的完整性约束。在事务开始和结束时,数据库必须处于一致的状态。如小李转账100元给小白,不管操作是否成功,小李和小白的账户总额是不变的。
- 隔离性:事务的执行是相互隔离的,即每个事务对其他事务是透明的。并发执行的多个事务不会相互干扰,每个事务感知不到其他事务的存在。
- 持久性:一旦事务提交成功,事务中的所有操作都必须持久化到数据库中。
24. 事务的隔离级别有哪些
- 读未提交:最低的隔离级别。事务可以读取到其他事务尚未提交的数据,可能会出现脏读、不可重复读和幻读问题。
- 读已提交:事务只能读取到已经提交的数据。但在同一事务中,多次读取同一行的数据结果可能会不一致,可能会出现不可重复读和幻读问题。
- 可重复读:在同一个事务内,多次读取同一行的数据结果始终保持一致。MySQL的默认隔离级别就是可重复读。通过使用MVCC机制来实现,在读操作期间不会看到其他事务的修改,有效地解决了不可重复读问题。
- 串行化:最高的隔离级别。事务串行执行,避免了脏读、不可重复读和幻读等问题。但是并发性能较差,可能导致大量的锁竞争和资源争用。
25. 讲讲你对MVCC的理解
MVCC是一种并发控制策略,它在多个事务同时执行时,确保数据库的一致性和隔离性。MVCC通过为每个事务创建数据的不同版本,避免了锁竞争问题。
工作原理
- 每条数据行都有一个隐藏的版本号或时间戳,记录该行的创建或最后修改时间。
- 当事务开始,它会获取一个唯一的事务ID,作为其开始时间戳。
- 在读取数据时,事务只能访问在其开始时间戳之前已提交的数据。这个版本的数据在事务开始前就已存在。
- 当事务更新数据,会创建新版本的数据,将更新后的数据写入新的数据行,并将事务ID与新版本关联。
- 其他事务可以继续访问旧版本的数据,不受正在进行的更新事务影响。这种机制被称为快照读。
- 当事务提交,其所有修改才对其他事务可见。此时,新版本的数据成为其他事务读取的数据。
26. Undo log是如何回滚事务的
数据库中,Undo Log通常用于实现事务的回滚操作
首先,获取事务的回滚指针或Undo Log的起始位置。
从Undo Log的末尾开始逆向扫描,按照事务操作的逆序依次处理每个日志记录。
然后,针对 INSERT 操作,执行 DELETE 操作来撤销插入的数据。对于 UPDATE 操作,使用Undo Log 中记录的旧值将数据还原到之前的状态。
在回滚过程中,对于已经提交的其他事务所做的修改需要跳过,只处理属于当前回滚事务的 Undo Log 记录。
按照逆序依次处理所有的日志记录,直到达到回滚指针位置或 Undo Log 的起始位置。
完成后,清除或标记已回滚的 Undo Log 记录。
27. 讲讲主从复制原理与延迟
MySQL 的主从复制原理如下:
首先,主库将变更写入 binlog 日志。
从库连接到主库后,有一个 IO 线程负责将主库的 binlog 日志复制到自己本地,并写入到中继日志中。
然后,从库中有一个 SQL 线程会从中继日志读取 binlog,并执行其中的 SQL 内容,即在从库上再次执行一遍。
主从延迟的原因有哪些
- 主库的从库太多,主库需要将 binlog 日志传输给多个从库,导致复制延迟。
- 在从库执行的 SQL 中存在慢查询语句,会导致整体复制进程的延迟。
- 如果主库的读写压力过大,会导致主库处理 binlog 的速度减慢,进而影响复制延迟。
如何减少主从复制的延迟
- 减少从库的数量,降低主库的负载,减少复制延迟。
- 优化慢查询语句,减少从库执行SQL 的延迟。
- 对主库进行性能优化,减少主库的读写压力,提高 binlog 写入速度。
28. 自增主键会遇到什么问题
- 插入性能问题:对于非常高并发的插入操作,自增主键可能会成为性能瓶颈。因为每次插入新记录时,都需要获取一个新的自增ID,这个操作是串行的,无法并发执行。
- 主键耗尽:如果表的记录非常多,可能会出现自增主键耗尽的情况。尤其是对于定义为整型的自增主键,如果插入的记录数超过了整型的最大值,就无法再插入新的记录。
- 分布式系统问题:在分布式系统中,如果多个数据库节点都需要生成自增主键,就需要保证生成的主键在全局是唯一的。这通常需要引入额外的机制或工具,比如分布式ID生成器。
29. 了解MySQL锁升级吗
MySQL锁升级是指在并发事务执行过程中,当某个事务无法满足当前所需的锁级别时,系统会自动将该事务的锁级别升级为更高级别的锁。
例如以下两种常见的锁升级场景:
- 当某个事务需要获取的是行级锁,但由于并发冲突或其他原因无法满足事务的要求时,系统会将该事务的锁级别升级为表级锁,从而保证数据的一致性和并发性能。
- 当某个事务操作的对象不符合行锁加锁规则,比如没有走索引或非唯一索引记录数达到一定数量,系统也会将该事务的锁级别升级为表级锁,以确保数据的完整性和一致性。
30. 如何优化深分页limit 1000000
以下查询可能会非常慢:
SELECT * FROM table ORDER BY id LIMIT 1000000, 10;
这是因为 MySQL 需要先扫描 1000000 行数据,然后再返回后面的 10 行数据。
- 使用覆盖索引: 覆盖索引可以让 MySQL 在索引中获取所有需要的数据,而无需回表查询。这可以大大提高查询速度。
- 记住上次的位置: 如果你的应用程序可以记住上次查询的最后一个 ID,那么你可以使用 WHERE 子句来避免扫描大量数据。
- 使用分页插件: 有些数据库框架提供了分页插件,可以自动优化分页查询。
- 避免深分页: 在设计应用程序时,尽量避免深分页。例如,你可以提供搜索功能,让用户快速找到他们需要的数据,而不是浏览大量的页面。
31. MySQL 有哪些高可用方案
- 主从复制:这是最常见的高可用方案。主库负责处理写操作,并将数据变更记录到binlog日志。从库将主库的binlog复制到自己的中继日志,然后执行中继日志中的事件,以达到与主库数据一致的目的。当主库出现故障时,可以将从库提升为新的主库,实现服务的高可用。
- 集群:MySQL集群是一个高可用、高性能的数据库集群解决方案。它使用了共享无关的架构,可以在节点之间自动分割和复制数据,实现了数据的高可用和高性能。
32. 如何解决MySQL死锁问题
解决死锁问题的方法通常有以下几种:
- 调整事务隔离级别:通过将事务隔离级别降低为读未提交(或读已提交,可以减少死锁的发生概率。但是要注意隔离级别的降低可能引发脏读、不可重复读等数据一致性问题,在选择时需要权衡利弊。
- 优化查询和事务逻辑:分析造成死锁的原因,优化查询语句和事务逻辑,尽量缩短事务持有锁的时间,减少死锁的可能性。比如按照相同的顺序获取锁,避免跨事务的循环依赖等。
- 使用行级锁:行级锁可以较小地限制锁的范围,从而减少死锁的可能性。将表的锁粒度调整为行级别,可以减少事务之间的冲突。
- 设置合理的超时时间和重试机制:当发生死锁时,可以设置适当的超时时间,在一定时间内尝试解锁操作。如果超过设定的时间仍未成功,则进行死锁处理,如终止较早请求的事务或进行回滚等。
33. Binlog有几种录入格式与区别
MySQL的Binlog有三种录入格式,分别是Statement格式、Row格式和Mixed格式
Statement格式:
- 将SQL语句本身记录到Binlog中。
- 记录的是在主库上执行的SQL语句,从库通过解析并执行相同的SQL来达到复制的目的。
- 简单、易读,节省存储空间。
- 但是,在某些情况下,由于执行计划或函数等因素的影响,相同的SQL语句在主从库上执行结果可能不一致,导致复制错误。
Row格式:
- 记录被修改的每一行数据的变化。
- 不记录具体的SQL语句,而是记录每行数据的变动情况,如插入、删除、更新操作前后的值。
- 保证了复制的准确性,不受SQL语句执行结果的差异影响,适用于任何情况。
- 但是,相比Statement格式,Row格式会占用更多的存储空间。
Mixed格式:
- Statement格式和Row格式的结合,MySQL自动选择适合的格式。
- 大多数情况下使用Statement格式进行记录,但对于无法保证安全复制的情况,如使用非确定性函数、触发器等,会自动切换到Row格式进行记录。
- 结合了两种格式的优势,既减少了存储空间的占用,又保证了复制的准确性。
我们需要根据实际需求和应用场景,选择适合的Binlog录入格式非常重要。
- Statement格式适用于简单的SQL语句,对存储空间要求较高;
- Row格式适用于需要精确复制的场景;
- Mixed格式是综合考虑两种格式的优势而出现的折中方案。
34. 说下你对分库分表的理解
MySQL的分库分表拆分策略通常包括垂直拆分和水平拆分两种方式
- 水平切分:水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。当一个表的数据不断增多时,Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓存单个数据库的压力。
- 垂直切分:垂直切分是将一张表按列切分成多个表,通常是按照列的关系密集程度进行切分,也可以利用垂直切分将经常被使用的列和不经常被使用的列切分到不同的表中。
分库分表面临的问题和解决方案如下
- 数据一致性:由于数据被分布到不同的数据库和表中,分库分表涉及跨节点的事务,需要确保数据的一致性。可以采用两阶段提交(2PC)协议、最终一致性方案或者基于分布式事务的工具来管理分布式事务,确保数据的一致性。
- 跨分片查询:当业务需要跨多个分片进行查询时,可能会面临性能问题和复杂的查询逻辑。可以使用分布式查询引擎、数据聚合、缓存和分布式计算框架等技术来处理跨分片查询需求,提高查询效率和简化查询逻辑。
- 全局唯一性约束:在分库分表环境下,全局唯一性约束可能受到挑战。可以采用分布式唯一ID生成器(如Snowflake算法)来生成全局唯一ID,避免冲突。
35. 高并发场景下,如何安全修改同一行数据
- 乐观锁:在数据表中添加一个版本号(或者时间戳)字段,每次更新数据时都会检查该字段的值。当多个并发的请求同时修改同一行数据时,只有一个请求能够成功执行更新操作,其他请求需要重新检查数据是否被修改过。如果数据没有被修改,那么它们可以重新尝试更新;如果数据已经被修改,则这些请求需要触发重试等相应的冲突处理逻辑。
- 悲观锁:在读取数据之前,使用数据库提供的锁机制,如SELECT ... FOR UPDATE语句,将要修改的行数据进行加锁。这样,其他并发的请求在读取相同行数据时会被阻塞,直到锁释放。这种方法能够确保同一时间只有一个请求在修改数据,但是会影响系统的并发性能。
- 分布式锁:通过使用分布式锁服务,如Redis的SETNX命令或ZooKeeper的临时节点,来实现对行级数据的互斥访问。在修改数据前先尝试获取锁,获取成功后执行数据修改操作,修改完成后释放锁。其他请求在获取锁失败时可以等待或执行相应的冲突处理逻辑。
- 事务:将对同一行数据的修改操作封装在数据库事务中。在事务中,数据库会自动处理并发修改的冲突,通过锁定相应的数据行来确保数据的一致性和完整性。并发的请求会被串行化执行,保证每个请求都能正确读取并修改数据。
36. 数据库 cpu 飙升,你会如何处理
- 查询性能问题:某些查询可能没有被正确地优化,导致查询执行时间过长,从而占用大量的CPU资源。可以通过查看慢查询日志和执行计划来分析问题查询,并进行索引优化、重写查询语句或调整数据库配置等方式来改善查询性能。
- 数据库连接问题:如果存在大量的数据库连接并发访问,可能会造成CPU负载过高。可以检查应用程序连接池的配置情况、数据库连接数限制以及是否有闲置的连接未关闭等问题,并进行相应调整。
- 锁和死锁问题:并发事务之间的锁竞争或死锁可能导致CPU飙升。可以通过查看数据库的锁状态、死锁日志以及事务并发控制的设置来解决锁相关的问题。
- 配置问题:不合理的数据库配置可能导致CPU资源浪费和效率低下。可以检查MySQL的配置参数,如缓冲区大小、并发连接数、线程池大小等是否合理设置,并进行相应调整。
- 资源竞争:如果服务器的物理资源(如内存、磁盘I/O)不足或受限,可能会导致CPU过度使用。可以通过监控系统资源使用情况,调整或增加资源配置,以满足数据库的需求。
37. 一条SQL的执行过程是怎样的
- 连接器:
- 客户端与数据库建立连接,并发送 SQL 语句给数据库服务。
- 连接器验证客户端的身份和权限,确保用户有足够的权限执行该 SQL 语句。
- 查询缓存:
- 连接器首先检查查询缓存,尝试找到与当前 SQL 语句完全相同的查询结果。
- 如果在缓存中找到匹配的结果,查询缓存直接返回结果,避免了后续的执行过程。
- 分析器:
- 若查询不命中缓存,连接器将 SQL 语句传递给分析器进行处理。
- 分析器对 SQL 语句进行语法分析,确保语句的结构和语法正确。
- 分析器还会进行语义分析,检查表、列、函数等对象的存在性和合法性,并进行权限验证。
- 优化器:
- 分析器将经过验证的 SQL 语句传递给优化器。
- 优化器根据统计信息和数据库的规则,生成多个可能的执行计划,这些计划包括不同的索引选择、连接顺序、筛选条件等。
- 目的是选出最优的执行路径以提高查询性能。
- 执行器:
- 优化器选择一个最优的执行计划,并将其传递给执行器。
- 执行器根据执行计划执行具体的查询操作。
- 它负责调用存储引擎的接口,处理数据的存储、检索和修改。
- 执行器会根据执行计划从磁盘或内存中获取相关数据,并进行联接、过滤、排序等操作,生成最终的查询结果。
- 存储引擎:
- 执行器将查询请求发送给存储引擎组件。
- 存储引擎组件负责具体的数据存储、检索和修改操作。
- 存储引擎根据执行器的请求,从磁盘或内存中读取或写入相关数据。
- 返回结果:
- 存储引擎将查询结果返回给执行器。
- 执行器将结果返回给连接器。
- 最后,连接器将结果发送回客户端,完成整个执行过程。
需要注意的是,查询缓存在一些场景下可能不太适用,因为它有一定的缺陷和开销。MySQL 8.0 版本开始,默认情况下查询缓存已被废弃。因此,在实际应用中,需要权衡是否使用查询缓存。
38. 为什么MySQL要默认使用RR隔离级别?
在 RU 级别下,可能会出现脏读、幻读、不可重复读等问题。
在 RC 级别下,解决了脏读的问题,但仍存在幻读、不可重复读的问题。
在 RR 级别下,解决了脏读和不可重复读的问题,但仍存在幻读的问题。
在 Serializable 隔离级别下,解决了脏读、幻读、不可重复读全部问题。
MySQL 默认使用RR(可重复读)隔离级别的原因是基于历史和技术考虑。
MySQL 主从复制是通过 binlog 日志进行数据同步的,而在早期的版本中 binlog 记录的是SQL语句的原文。这个时候就会有一个问题,如果此时 binlog 格式设置为 statement 格式时,MySQL 可能会在从库执行 SQL 的逻辑与主库不一致。比如:delete from user where a >= 13 and b<= '2024-04-21' limit 1;
39. 为什么 Oracle 选择的是 RC 级别?
Read-Only 隔离级别类似于序列化隔离级别,但只读事务甚至不允许在事务中进行数据修改。
很显然,在这三种隔离级别中,Serializable 和 Read-Only 显然都不适合作为默认隔离级别,Oracle 只剩下 Read Committed 这个选择。