1. 事务有哪几种类型,它们之间有什么区别?
事务按ACID特性和实现方式,主要分为以下几类:
- 扁平事务(Flat Transaction):最常见的事务类型,由一组操作组成,要么全部提交,要么全部回滚,无嵌套结构。
- 嵌套事务(Nested Transaction):允许在事务内部再开启子事务,子事务可独立提交或回滚,但父事务提交后子事务才真正生效;MySQL InnoDB不原生支持嵌套事务,可通过SAVEPOINT模拟。
- 分布式事务(Distributed Transaction):跨多个数据库或服务的事务,需通过两阶段提交(2PC)、三阶段提交(3PC)等协议保证一致性,常见于微服务架构。
- 长事务(Long Transaction):执行时间较长的事务,易导致锁竞争和性能问题,需避免在业务中使用。
2. MySQL的ACID特性分别是怎么实现的?
- 原子性(Atomicity):通过Undo Log实现,事务执行过程中产生的修改操作会记录到Undo Log,若事务回滚,可通过Undo Log撤销所有操作。
- 一致性(Consistency):由原子性、隔离性和持久性共同保证,同时依赖数据库约束(如主键、外键、唯一索引)和应用层逻辑。
- 隔离性(Isolation):通过锁机制和MVCC(多版本并发控制)实现,不同隔离级别通过锁和MVCC的组合,避免并发事务间的相互干扰。
- 持久性(Durability):通过Redo Log实现,事务提交前,修改操作会先写入Redo Log并持久化到磁盘,即使数据库崩溃,重启后可通过Redo Log恢复数据。
3. 谈谈MySQL的事务隔离级别
MySQL InnoDB支持四种隔离级别,从低到高依次为:
- 读未提交(READ UNCOMMITTED):事务可读取其他事务未提交的数据,会导致脏读、不可重复读、幻读。
- 读已提交(READ COMMITTED):事务只能读取其他事务已提交的数据,可避免脏读,但仍存在不可重复读、幻读。
- 可重复读(REPEATABLE READ):MySQL InnoDB默认隔离级别,事务执行期间多次读取同一数据的结果一致,可避免脏读、不可重复读,通过Next-Key Lock避免幻读。
- 串行化(SERIALIZABLE):强制事务串行执行,所有读操作加共享锁,写操作加排他锁,可避免所有并发问题,但性能极低。
4. MySQL的事务隔离级别是怎么实现的?
- 读未提交:直接读取最新数据,无锁和MVCC,性能最高但隔离性最差。
- 读已提交:通过MVCC实现,每次读取时生成最新的Read View,仅能看到已提交的数据,避免脏读。
- 可重复读:通过MVCC实现,事务开始时生成唯一的Read View,整个事务期间使用该Read View,保证同一事务内多次读取结果一致;同时通过Next-Key Lock避免幻读。
- 串行化:对所有读操作加共享锁,写操作加排他锁,事务间串行执行,无并发但完全隔离。
5. 事务可以嵌套吗?
MySQL InnoDB不原生支持嵌套事务,但可通过SAVEPOINT实现类似效果:
- 执行SAVEPOINT sp1设置保存点,后续可通过ROLLBACK TO sp1回滚到该保存点,仅撤销保存点之后的操作,不影响保存点之前的操作。
- 注意:SAVEPOINT并非真正的嵌套事务,父事务提交后子事务才生效,父事务回滚子事务也会全部回滚。
6. 如何实现可重复读?
InnoDB通过MVCC(多版本并发控制)实现可重复读:
- 事务开始时,生成唯一的Read View,记录当前活跃事务的ID和最小未提交事务ID。
- 读取数据时,根据Read View判断数据版本的可见性,仅能看到事务开始前已提交的数据和本事务内修改的数据。
- 同一事务内多次读取同一数据时,始终使用同一个Read View,保证读取结果一致,避免不可重复读。
7. 如何解决幻读问题?
幻读是指同一事务内,两次查询的结果集行数不同(如第一次查询10条,第二次查询12条,因其他事务插入了新数据)。InnoDB通过Next-Key Lock(间隙锁+行锁)解决幻读:
- 对查询范围内的所有行加行锁,同时对行之间的间隙加间隙锁,阻止其他事务在间隙中插入新数据,保证查询结果集行数一致。
- 注意:仅在可重复读及以上隔离级别下,Next-Key Lock才生效,可避免幻读。
8. MySQL事务如何回滚?
- 显式回滚:执行ROLLBACK语句,撤销当前事务的所有操作,释放锁和Undo Log。
- 隐式回滚:事务执行过程中发生错误(如死锁、约束冲突)或数据库崩溃,MySQL会自动回滚事务,通过Undo Log恢复数据。
- 核心依赖:Undo Log记录了事务执行前的数据版本,回滚时通过Undo Log将数据恢复到事务开始前的状态。
9. 了解数据库的锁吗?
数据库锁用于控制并发访问,保证数据一致性,主要分为以下几类:
- 按粒度分:行级锁(InnoDB)、表级锁(MyISAM)、页级锁(较少用)。
- 按性质分:共享锁(S锁,读锁,允许多个事务同时读,不允许写)、排他锁(X锁,写锁,独占资源,不允许其他事务读写)。
- 按实现分:意向锁(表级锁,表明事务即将对表内行加锁,提升锁检测效率)、间隙锁(锁定行之间的间隙,防止插入)、Next-Key Lock(行锁+间隙锁,避免幻读)。
10. 介绍一下间隙锁,InnoDB中行级锁是怎么实现的?
- 间隙锁(Gap Lock):锁定索引记录之间的间隙,不锁定具体行,用于防止其他事务在间隙中插入新数据,避免幻读。
- 行级锁实现:InnoDB基于索引实现行级锁,若查询未命中索引,会退化为表级锁;行级锁分为共享锁和排他锁,通过锁的兼容性控制并发访问。
- Next-Key Lock:行锁+间隙锁的组合,锁定索引记录和其前后的间隙,是InnoDB在可重复读隔离级别下避免幻读的核心机制。
11. 数据库在什么情况下会发生死锁?
死锁是指两个或多个事务互相持有对方所需的锁,且均不释放已持有的锁,导致所有事务永久阻塞。常见场景:
- 事务A持有行1的X锁,请求行2的X锁;事务B持有行2的X锁,请求行1的X锁,形成循环等待。
- 多个事务按不同顺序更新同一批数据,导致锁请求顺序冲突。
- 长事务持有锁不释放,其他事务请求锁时形成死锁。
12. 说说数据库死锁的解决办法
- 预防死锁:按固定顺序访问数据(如按主键升序更新),避免循环等待。避免长事务,及时释放锁。降低隔离级别(如从可重复读改为读已提交),减少锁竞争。
- 检测与解除:MySQL通过死锁检测机制(如等待图)定期检测死锁,检测到后主动回滚代价较小的事务(如执行时间短的事务)。设置锁等待超时时间(innodb_lock_wait_timeout),超时后自动回滚事务。
13. 说一说你对数据库优化的理解
数据库优化是一个系统性工程,核心目标是提升查询效率、降低资源消耗、保证数据一致性,主要分为四个层面:
- SQL与索引优化:优化慢查询、添加合适的索引、避免全表扫描。
- 表结构优化:合理设计表结构(如拆分大表、避免冗余字段)、选择合适的数据类型(如用INT代替VARCHAR存储数字)。
- 数据库配置优化:调整内存参数(如innodb_buffer_pool_size)、日志参数(如Redo Log大小)、并发参数(如max_connections)。
- 架构优化:读写分离、分库分表、缓存层(如Redis)、数据库集群(如主从复制、分片)。
14. 该如何优化MySQL的查询?
- 优化SQL语句:避免SELECT *,只查询需要的字段,减少数据传输和内存消耗。避免在WHERE子句中对索引列使用函数或表达式,导致索引失效。拆分复杂查询,用简单查询替代子查询和多表连接。
- 添加合适的索引:对WHERE、JOIN、ORDER BY子句中的列添加索引,优先选择选择性高的列。避免创建过多索引,索引会降低写入性能。
- 使用EXPLAIN分析执行计划:查看查询是否命中索引、是否全表扫描、是否有临时表和文件排序,针对性优化。
15. 怎样插入数据才能更高效?
- 批量插入:使用INSERT INTO ... VALUES (...), (...)批量插入,减少事务提交次数和日志写入开销。
- 关闭自动提交:批量插入前关闭自动提交(SET autocommit=0),插入完成后统一提交,避免频繁事务提交。
- 禁用索引和约束:批量插入前禁用索引和外键约束,插入完成后重建索引,提升插入速度(注意:仅适用于离线批量插入)。
- 调整数据库参数:增大innodb_buffer_pool_size、innodb_log_file_size,提升写入性能。
16. 表中包含几千万条数据该怎么办?
- 分表分库:水平分表:按主键或时间范围拆分表(如按用户ID哈希分表、按月份分表),降低单表数据量。垂直分表:将大表按字段拆分(如将常用字段和不常用字段拆分到不同表),减少单表宽度。分库:将不同业务模块的数据拆分到不同数据库,提升并发处理能力。
- 读写分离:主库负责写入,从库负责读取,分担读压力。
- 缓存层:用Redis缓存热点数据,减少数据库查询次数。
- 归档历史数据:将不常用的历史数据归档到冷存储,避免影响热数据查询性能。
17. MySQL的慢查询优化有了解吗?
- 开启慢查询日志:设置slow_query_log=1,记录执行时间超过long_query_time(默认10秒)的查询,定位慢查询。
- 分析慢查询日志:用mysqldumpslow、pt-query-digest等工具分析慢查询,找出高频慢查询和性能瓶颈。
- 优化慢查询:对慢查询添加索引,避免全表扫描。优化SQL语句,减少子查询和多表连接。调整数据库参数,提升查询性能(如增大join_buffer_size)。
18. 说一说你对explain的了解
EXPLAIN是MySQL的执行计划分析工具,用于查看查询语句的执行过程,核心字段含义:
- id:查询的执行顺序,id越大优先级越高。
- select_type:查询类型(如SIMPLE、SUBQUERY、DERIVED)。
- table:查询涉及的表。
- type:连接类型(如ALL、index、range、ref、eq_ref、const),从差到好,ALL表示全表扫描,const表示主键或唯一索引查询。
- key:实际使用的索引,若为NULL表示未命中索引。
- rows:MySQL预估需要扫描的行数,行数越少性能越好。
- Extra:额外信息(如Using index、Using where、Using filesort、Using temporary),Using filesort和Using temporary会严重影响性能。
19. 什么是MVCC?它的作用是什么?
MVCC(多版本并发控制)是InnoDB实现隔离性的核心机制,通过保存数据的历史版本,让读操作不加锁,同时保证事务隔离。
- 核心原理:每行数据隐藏两个字段——DB_TRX_ID(最后修改该数据的事务ID)和DB_ROLL_PTR(指向Undo Log中历史版本的指针)。
- 作用:实现可重复读和读已提交隔离级别,避免脏读、不可重复读。提升并发性能,读操作不加锁,写操作仅锁定必要的行。
20. 什么是索引?索引的类型有哪些?
索引是一种数据结构,用于快速定位数据,提升查询效率。常见类型:
- B+树索引:InnoDB默认索引类型,支持范围查询和排序,是最常用的索引。
- 哈希索引:基于哈希表实现,仅支持等值查询,不支持范围查询,InnoDB的自适应哈希索引属于此类。
- 全文索引:用于全文检索,支持对文本内容的关键词查询。
- 空间索引:用于地理空间数据(如经纬度)的查询。
21. 索引为什么能提升查询效率?
- 索引将无序的数据变为有序的B+树结构,查询时可通过二分查找快速定位数据,避免全表扫描。
- 覆盖索引(查询的字段都在索引中)可直接从索引中获取数据,无需回表查询,进一步提升性能。
22. 什么是覆盖索引?
覆盖索引是指查询的所有字段都包含在索引中,MySQL可直接从索引中获取数据,无需回表查询主键索引,大幅提升查询效率。
- 示例:SELECT name FROM user WHERE age=18,若(age, name)是联合索引,该查询可直接从索引中获取name,无需回表。
23. 什么是联合索引?联合索引的最左前缀原则是什么?
联合索引是指对多个列创建的索引,如(a, b, c)。最左前缀原则是指:
- 查询时,只有从索引的最左列开始连续使用索引列,才能命中索引。
- 示例:联合索引(a, b, c),查询WHERE a=1 AND b=2可命中索引,查询WHERE b=2无法命中索引。
24. 什么是索引失效?常见的索引失效场景有哪些?
索引失效是指查询无法使用已创建的索引,导致全表扫描。常见场景:
- 在WHERE子句中对索引列使用函数或表达式(如WHERE YEAR(create_time)=2024)。
- 违反最左前缀原则(如联合索引(a, b),查询WHERE b=1)。
- 使用OR连接条件,其中一个条件未命中索引。
- 数据量过小,MySQL认为全表扫描比索引查询更快。
25. 什么是主从复制?主从复制的原理是什么?
主从复制是指将主库的数据同步到从库,实现读写分离和高可用。
- 原理:主库将数据修改操作记录到Binlog。从库的I/O线程读取主库的Binlog,写入到Relay Log。从库的SQL线程读取Relay Log,执行其中的操作,同步数据到从库。
26. 主从复制的延迟问题如何解决?
- 优化主库写入性能:减少主库的写入压力,避免频繁的大事务。
- 调整从库参数:增大innodb_buffer_pool_size、slave_parallel_workers,提升从库的执行效率。
- 使用半同步复制:主库等待至少一个从库确认接收Binlog后再提交,减少数据丢失风险,但会增加延迟。
- 分库分表:拆分大表,减少单表数据量,提升复制速度。
27. 什么是分库分表?分库分表的策略有哪些?
分库分表是指将一个大数据库或大表拆分为多个小数据库或小表,提升并发处理能力和查询性能。
- 水平分表:按主键或时间范围拆分表(如按用户ID哈希分表、按月份分表)。
- 垂直分表:将大表按字段拆分(如将常用字段和不常用字段拆分到不同表)。
- 分库:将不同业务模块的数据拆分到不同数据库,提升并发处理能力。
28. 什么是数据库的三大范式?
- 第一范式(1NF):列不可再分,保证原子性。
- 第二范式(2NF):在1NF的基础上,非主键列完全依赖于主键,而非主键的一部分。
- 第三范式(3NF):在2NF的基础上,非主键列不依赖于其他非主键列,避免传递依赖。
- 注意:实际开发中可根据业务需求适当反范式,如添加冗余字段提升查询性能。
29. 什么是视图?视图的作用是什么?
视图是一个虚拟表,基于一个或多个表的查询结果创建,本身不存储数据。
- 作用:简化复杂查询,将多表连接和子查询封装为视图。控制数据访问权限,只允许用户访问视图中的数据,而非原始表。保护敏感数据,隐藏原始表的结构和字段。
30. 什么是存储过程?存储过程的优缺点是什么?
存储过程是一组预编译的SQL语句,存储在数据库中,可通过调用执行。
- 优点:减少网络传输,一次调用即可执行多条SQL语句。提升性能,预编译的SQL语句可重复使用。封装业务逻辑,保证数据一致性。
- 缺点:可移植性差,不同数据库的存储过程语法不同。调试和维护困难,业务逻辑分散在数据库中。过度使用会导致数据库压力过大。
全部评论
(7) 回帖