10|数据库索引:为什么 MySQL 用 B+ 树而不用 B 树?
- 什么是覆盖索引?
查询的列被索引完全覆盖. 好处是这样就不需要回表,直接通过索引查出值. - 什么是聚簇索引/非聚簇索引?
聚簇索引:索引叶子节点存储数据, 否则就是非聚簇索引. - 什么是哈希索引?MySQL InnoDB 引擎怎么创建一个哈希索引?
基于哈希算法建立的索引; innodb 不支持哈希索引 - 什么是回表?如何避免回表?
回表是指在数据库(尤其是MySQL的InnoDB存储引擎)中,使用二级索引(非主键索引)查询时,先通过二级索引定位到主键值,然后再根据主键到聚簇索引(主键索引)中查找完整的数据行的过程。这个“回到主键表”查找的过程就叫“回表”。 如何避免回表? 1.覆盖索引(索引覆盖) 只查询索引中已有的字段,不需要回表。 2.将查询字段全部包含在二级索引中 设计联合索引时,把常用的查询字段都放进索引里。 3.使用主键索引查询 直接用主键查找,不存在回表问题。 - 树的高度和查询性能是什么关系?
树越高查询性能越差 - 什么是索引最左匹配原则?
从最左边的列开始匹配,比如以abc列建立的索引, 查询a、ab、abc 都会匹配. - 范围查询、Like 之类的查询怎么影响数据库使用索引?
范围查询会中断最左匹配原则; 范围查询如果某列值都是>0, where < 0 则不会只用索引. !=, like 可能不使用索引. 数据区分度不高的. - 索引是不是越多越好?
不是, 有代价 - 使用索引有什么代价?
存储空间、写操作性能、内存消耗 - 如何选择合适的索引列?组合索引里面怎么确定列的顺序?状态类的列是否适合作为索引的列?
优先选择覆盖索引 组合索引原则: 区分度高、where条件之后的放在前列, 包含 orderby group 排序分组列 范围查询列靠后 状态类的列区分度低, 不适合进入索引. - 为什么 MySQL 使用 B+ 树作为索引的数据结构?为什么不用 B 树?为什么不用红黑树?为什么不用二叉平衡树?为什么不用跳表?
- 红黑树、二叉树类, 同等数据量, 高度更高, 性能更差. - 跳表, 极端情况下会退化为链表,性能不稳定,且需要更多内存,查找效率从 O(log n) 变为 O(n)。 - b树, 对范围查询不友好,b+树 叶子节点通过链表相连; 非叶子节点存储了数据, 如果内存中放不下非叶子节点, 会增加回表. - NULL 对索引有什么影响?
可能影响查询性能 - 唯一索引是否允许多个NULL 值?
允许, NULL 表示为 未知行为
11|SQL 优化:如何发现 SQL 中的问题?
- 请你解释一下 EXPALIN 命令
查看sql解释计划, 重要字段 type: 查询所需行的方式; key: 实际使用的索引 - 你有优化过 SQL 吗?具体是怎么优化的?/ 3. 你有没有优化过索引?怎么优化的?
explain sql 查看解释计划, 是否使用了覆盖索引、where、orderby 字段索引是否覆盖; 分页查询大偏移量使用游标 - 怎么优化 COUNT 查询?
mysql 对于count(*) 没有优化, 所以用 redis 或 额外表存储汇总值 - 怎么优化 ORDER BY?
将 order by 字段加入索引 - 怎么优化 LIMIT OFFSET 查询?
limit 变大后, 使用游标 - 为什么要尽量把条件写到 WHERE 而不是写到 HAVING 里面?
2次数据库操作, 变为一次 - 怎么给一张表添加新的索引/修改表结构?如果我的数据量很大呢?
停机变更/业务低谷/创建新表数据迁移, 基于表的数据量 - USE INDEX/FORCE INDEX/IGNORE INDEX 有什么效果?
强制使用某索引
12|数据库锁:明明有行锁,怎么突然就加了表锁?
- 什么是行锁、表锁?什么时候加表锁?怎么避免?
针对行纪录加的锁就是行锁, 针对整个表加的锁就是表锁;表锁范围大于行锁.如果查询没有使用到索引, 就会加表锁. 利用索引. - 什么是乐观锁?怎么在 MySQL 里面实现一个乐观锁?
乐观锁:cas, 根据旧数据进行更新. - 什么是意向锁?可以举一个例子吗?
数据库内部管理的锁 - 什么是共享锁和排它锁?它们有什么特性?
排他锁是同类型线程互斥的 - 什么是两阶段加锁?
两阶段加锁是指在一个事务执行过程中,加锁和解锁分为两个阶段: 加锁阶段:事务在需要访问数据时不断加锁,但不释放任何锁。 解锁阶段:事务一旦开始释放锁,就不能再加任何新锁。 这样可以保证事务的隔离性,避免死锁和数据不一致。只有在事务结束(提交或回滚)时才会统一释放所有锁。 - 什么是记录锁、间隙锁和临键锁?
- 记录锁:锁住某一行记录,也叫行锁。比如用主键或唯一索引精确查找时加的锁。 - 间隙锁:锁住某个范围(间隙),但不包括已有的记录本身。用于防止其他事务在这个范围内插入新记录,防止幻读。 - 临键锁:同时锁住已有记录和相邻的间隙。大多数范围查询会加临键锁,既防止已有记录被修改,也防止新记录插入。(= 记录锁 + 间隙锁) - RR(可重复读)隔离级别下,为了防止幻读,会用到间隙锁和临键锁。 - RC(读已提交)隔离级别下通常只加记录锁,不加间隙锁和临键锁。 假设表 t 有主键值为 10、20、30 三条记录。 SELECT * FROM t WHERE id > 10 AND id < 20 FOR UPDATE; 此时 InnoDB 会在 (10, 20) 这个范围加间隙锁,防止其他事务在 10 和 20 之间插入新记录(比如插入 id=15)。 SELECT * FROM t WHERE id >= 10 AND id < 20 FOR UPDATE; 此时 InnoDB 会锁住 id=10 这条记录(记录锁),同时锁住 (10, 20) 这个范围(间隙锁),合起来就是临键锁,既防止已有记录被修改,也防止新记录插入。 如果 不加 FOR UPDATE: 普通的 SELECT 查询默认是不会加锁的,只是快照读(非锁定读),不会对数据加行锁或间隙锁。 - RC 级别有间隙锁和临键锁吗?
没有 - MySQL 是怎么在 RR 级别下解决幻读的?
通过锁住查询范围, 防止其他事务插入数据 - 什么情况下会加临键锁?什么情况下会加间隙锁?什么时候加记录锁?
大多数情况下查询都会加临键锁, 使用了主键、唯一索引命中则只会加记录锁; 已有数据范围内查询会加间隙锁; 已有数据之外会加临键锁 - 唯一索引和普通索引会怎么影响锁?
命中数据会加记录锁; 否则只会加间隙锁 - 你遇到过什么死锁问题吗?怎么排查的?最终又是怎么解决的?
临键锁死锁, select where id = ? (> 表中最大id) for update then insert lastid + 1, 2..., 临键锁都是 (表中最大id, max] - 你有没有优化过锁?怎么优化的?
像上面的例子中, 不加锁, 直接插入数据, 如果发生唯一索引或者主键冲突则执行数据存在逻辑, 否则执行数据不存在逻辑
13|MVCC协议:MySQL 修改数据时,还能不能读到这条数据?
- 什么是 MVCC?为什么需要 MVCC?
多版本并发控制, 为了在提交读和可重复读隔离级别时, 提升读写并发 - 什么是隔离级别?隔离级别有哪几种?
简单讲隔离级别就是一个事务是否了解别的事务以及程度怎么样. - 什么是脏读、不可重复读、幻读?它们与隔离级别的关系是怎样的?
脏读是读到其他事务未提交的更改;不可重复读是指事务中先后读取到的同一个数据不一致;幻读是指一个事务中, 因为有另一个事务的插入或更新数据从而读到了变更. - 隔离级别是不是越高越好?
隔离级别越高数据一致性越强, 并发行越差. - 你们公司用的是什么隔离级别?为什么使用这个隔离级别?能不能使用别的隔离级别?
使用默认可重复读隔离级别;可以降低为提交读. - 你有没有改过隔离级别?为什么改?
可重复读可能会因为临键锁而发生死锁, 可以通过改成提交读, 因为只有rr 级别里才有临键锁.
14|数据库事务:事务提交了,你的数据就一定不会丢吗?
- 什么是 Buffer Pool; 什么是日志缓冲区
Buffer Pool 和 日志缓冲区是 InnoDB 的特性. Buffer Pool 是 InnoDB 的数据缓存区域,主要用于缓存数据页和索引页。 日志缓冲区, 用于暂存 Redo Log,主要目的是支持事务的持久性和崩溃恢复。 - 什么是 undo log?为什么需要 undo log?
回滚日志, mvcc 和事务回滚 需要 - 什么是 redo log?为什么需要 redo log?
重放日志, 用于数据库崩溃恢复、持久化更改,系统崩溃数据不丢失、事务提交时顺序写提高性能 - 什么是 binlog?它有几种模式?用来做什么?
二进制日志,3种模式:语句模式、行模式、混合模式;数据恢复、主从复制 - 事务是如何执行的?
事务开启, 执行查询,锁定 写入undo log buffer pool 更新值 写入redo log 事务提交, 根据参数决定是否刷盘 redo log, 写入binlog 刷新 buffer pool 到磁盘 - 什么是 ACID?隔离性和隔离级别是什么关系?你觉得哪个隔离级别满足这里的隔离性要 求?
事务的原子性、一致性、隔离性、持久性 innodb 可重复读解决了幻读, 所以可重复读和串行化都满足隔离性要求 - redo log 的刷盘时机有哪些?该如何选择?你们公司用的是哪个配置?为什么用这个配 置?
innodb_flush_log_at_trx_commit 0,1,2 分别是 每秒刷日志到磁盘、事务提交时刷日志到磁盘、提交给操作系统page cache 就不管了 - binlog 的刷盘时机有哪些?该如何选择?你们公司用的是哪个配置?为什么用这个配置?
sync_binlog 0 或 N, 分别代表写入 page cache 交给操作系统刷新 和 每 N 次提交事务(也会先写入到page cache) 刷新日志到磁盘 - 我的事务提交了,就一定不会丢吗?怎么确保一定不会丢?
redo log 同步参数=0, 可能会丢1s 日志, =2 写到 page cache 需要等操作系统刷盘, 不确定性更大. binlog 同步参数=0, 也是写入到page cache, 同理. 如果!=1 则提交N 次才会刷盘. 如果期间崩溃 binlog 也会丢. - 什么是 page cache?为什么不直接写到磁盘?
操作系统缓存页. 写磁盘太慢 - 在分布式环境下,当服务器告诉我写入成功的时候,一定写入成功了吗?如果服务器宕机了可能发生什么?
不一定, 取决于写入语义: 主节点写入、主节点子节点全部写入、主节点部分节点写入. 服务器重数据: sync_binlog=1 innodb_flush_log_at_trx_commit=1 重性能其他: innodb_flush_log_at_trx_commit=0 or 2 sync_binlog=N 如果服务器宕机了可能会丢失数据.
15|数据迁移:如何在不停机的情况下保证迁移数据的一致性? pass
16|分库分表主键生成:如何设计一个主键生成算法?
- 你们分库分表怎么生成主键的?
雪花算法 - 使用 UUID/数据库自增/雪花算法有什么优缺点?
uuid 不是自增的; 数据库自增不是全局的; 雪花算法全局自增 - 雪花算法是如何实现的?
一位保留+时间戳+机器id+序号 - 雪花算法是怎么做到全局唯一的?
时间递增不同时间产生的id肯定是不同的、机器id是不同的, 同一时刻不同机器产生的id 肯定也不同、同一时刻同一机器id,可以轻易控制序号 - 怎么解决雪花算法的序列号耗尽问题?
增加id 的长度, 或者序号耗尽后, 等待至多1毫秒 - 怎么解决雪花算法的数据堆积问题?
某一个时刻采用随机数作为起点, 作为序号产生的一部分 - 你有没有优化过主键生成的性能?怎么优化的?效果如何?
singleflight 批量取: - 你的主键生成的 ID 是严格递增的吗?不是递增有什么问题?
严格递增, 不是递增的容易造成数据页分裂 - 为什么我们一般使用自增主键?
唯一性保证:自增主键由数据库自动生成,保证每个记录的标识是唯一的,避免了手动指定主键可能导致的冲突问题。 高效索引:自增主键通常是整数类型(如 INT 或 BIGINT),占用空间小且递增有序。这种特性使得在 B+树索引(常见于关系型数据库如 MySQL)中插入和查询效率更高,因为新记录总是追加到索引的末尾,减少了索引分裂和重组的开销。 简单易用:自增主键无需开发者手动维护,数据库会自动为每条新记录分配一个递增的ID,简化了开发流程,降低了出错概率。 - 什么是页分裂?有什么缺点?
数据库数据因为插入新的数据造成结构重构, 可能划分成多个容量的数据页, 可能造成数据缓存命中率下降
17|分库分表分页查询:为什么你的分页查询又慢又耗费内存?
关键观念: 全局查询、游标查询
18|分库分表事务:如何同时保证分库分表、ACID及高性能?
- 在分库分表之后,如何解决事务问题?
分布式事务, 两阶段三阶段提交:适合对强一致性要求的场景,但性能较差。 本地事务 + 消息队列:适合对最终一致性要求的高并发场景。 TCC 和 Saga, 需要实现回滚:适合复杂业务场景,灵活性高,但开发成本较高。 分布式事务中间件:适合需要快速实现分布式事务的场景。
21|数据库综合应用:怎么保证数据库高可用、高性能?
- InnoDB 引擎的 buffer pool 是拿来做什么的?怎么优化它的性能?
缓存表和索引; 调整 innodb_buffer_pool_size (调整到 内存的 70% 或者 75%)和 innodb_buffer_pool_instances 参数 - 数据库里面有很多刷盘相关的参数,你都了解吗?调过吗?根据什么来调?
innodb_flush_log_at_trx_commit: redo log 刷盘时机 sync_binlog: bin log 刷盘时机