Tags

  • 技术

10|数据库索引:为什么 MySQL 用 B+ 树而不用 B 树?

  1. 什么是覆盖索引?
    查询的列被索引完全覆盖. 好处是这样就不需要回表,直接通过索引查出值.
    
  2. 什么是聚簇索引/非聚簇索引?
    聚簇索引:索引叶子节点存储数据, 否则就是非聚簇索引.
    
  3. 什么是哈希索引?MySQL InnoDB 引擎怎么创建一个哈希索引?
    基于哈希算法建立的索引; innodb 不支持哈希索引
    
  4. 什么是回表?如何避免回表?
    回表是指在数据库(尤其是MySQL的InnoDB存储引擎)中,使用二级索引(非主键索引)查询时,先通过二级索引定位到主键值,然后再根据主键到聚簇索引(主键索引)中查找完整的数据行的过程。这个“回到主键表”查找的过程就叫“回表”。
    如何避免回表?
    1.覆盖索引(索引覆盖)
    只查询索引中已有的字段,不需要回表。
    2.将查询字段全部包含在二级索引中
    设计联合索引时,把常用的查询字段都放进索引里。
    3.使用主键索引查询
    直接用主键查找,不存在回表问题。
    
  5. 树的高度和查询性能是什么关系?
    树越高查询性能越差
    
  6. 什么是索引最左匹配原则?
    从最左边的列开始匹配,比如以abc列建立的索引, 查询a、ab、abc 都会匹配.
    
  7. 范围查询、Like 之类的查询怎么影响数据库使用索引?
    范围查询会中断最左匹配原则;
    范围查询如果某列值都是>0, where < 0 则不会只用索引.
    !=, like 可能不使用索引.
    数据区分度不高的.
    
  8. 索引是不是越多越好?
    不是, 有代价
    
  9. 使用索引有什么代价?
    存储空间、写操作性能、内存消耗
    
  10. 如何选择合适的索引列?组合索引里面怎么确定列的顺序?状态类的列是否适合作为索引的列?
    优先选择覆盖索引
    组合索引原则:
    区分度高、where条件之后的放在前列,
    包含 orderby group 排序分组列
    范围查询列靠后
    状态类的列区分度低, 不适合进入索引.
    
  11. 为什么 MySQL 使用 B+ 树作为索引的数据结构?为什么不用 B 树?为什么不用红黑树?为什么不用二叉平衡树?为什么不用跳表?
    - 红黑树、二叉树类, 同等数据量, 高度更高, 性能更差.
    - 跳表, 极端情况下会退化为链表,性能不稳定,且需要更多内存,查找效率从 O(log n) 变为 O(n)。
    - b树, 对范围查询不友好,b+树 叶子节点通过链表相连; 非叶子节点存储了数据, 如果内存中放不下非叶子节点, 会增加回表.
    
  12. NULL 对索引有什么影响?
    可能影响查询性能
    
  13. 唯一索引是否允许多个NULL 值?
    允许, NULL 表示为 未知行为
    

11|SQL 优化:如何发现 SQL 中的问题?

  1. 请你解释一下 EXPALIN 命令
    查看sql解释计划, 重要字段 type: 查询所需行的方式; key: 实际使用的索引
    
  2. 你有优化过 SQL 吗?具体是怎么优化的?/ 3. 你有没有优化过索引?怎么优化的?
    explain sql 查看解释计划, 是否使用了覆盖索引、where、orderby 字段索引是否覆盖; 分页查询大偏移量使用游标
    
  3. 怎么优化 COUNT 查询?
    mysql 对于count(*) 没有优化, 所以用 redis 或 额外表存储汇总值
    
  4. 怎么优化 ORDER BY?
    将 order by 字段加入索引
    
  5. 怎么优化 LIMIT OFFSET 查询?
    limit 变大后,  使用游标
    
  6. 为什么要尽量把条件写到 WHERE 而不是写到 HAVING 里面?
    2次数据库操作, 变为一次
    
  7. 怎么给一张表添加新的索引/修改表结构?如果我的数据量很大呢?
    停机变更/业务低谷/创建新表数据迁移, 基于表的数据量
    
  8. USE INDEX/FORCE INDEX/IGNORE INDEX 有什么效果?
    强制使用某索引
    

12|数据库锁:明明有行锁,怎么突然就加了表锁?

  1. 什么是行锁、表锁?什么时候加表锁?怎么避免?
    针对行纪录加的锁就是行锁, 针对整个表加的锁就是表锁;表锁范围大于行锁.如果查询没有使用到索引, 就会加表锁. 利用索引.
    
  2. 什么是乐观锁?怎么在 MySQL 里面实现一个乐观锁?
    乐观锁:cas, 根据旧数据进行更新.
    
  3. 什么是意向锁?可以举一个例子吗?
    数据库内部管理的锁
    
  4. 什么是共享锁和排它锁?它们有什么特性?
    排他锁是同类型线程互斥的
    
  5. 什么是两阶段加锁?
    两阶段加锁是指在一个事务执行过程中,加锁和解锁分为两个阶段:
    加锁阶段:事务在需要访问数据时不断加锁,但不释放任何锁。
    解锁阶段:事务一旦开始释放锁,就不能再加任何新锁。
    这样可以保证事务的隔离性,避免死锁和数据不一致。只有在事务结束(提交或回滚)时才会统一释放所有锁。
    
  6. 什么是记录锁、间隙锁和临键锁?
    - 记录锁:锁住某一行记录,也叫行锁。比如用主键或唯一索引精确查找时加的锁。
    - 间隙锁:锁住某个范围(间隙),但不包括已有的记录本身。用于防止其他事务在这个范围内插入新记录,防止幻读。
    - 临键锁:同时锁住已有记录和相邻的间隙。大多数范围查询会加临键锁,既防止已有记录被修改,也防止新记录插入。(= 记录锁 + 间隙锁)
    - 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 查询默认是不会加锁的,只是快照读(非锁定读),不会对数据加行锁或间隙锁。
    
  7. RC 级别有间隙锁和临键锁吗?
    没有
    
  8. MySQL 是怎么在 RR 级别下解决幻读的?
    通过锁住查询范围, 防止其他事务插入数据
    
  9. 什么情况下会加临键锁?什么情况下会加间隙锁?什么时候加记录锁?
    大多数情况下查询都会加临键锁, 使用了主键、唯一索引命中则只会加记录锁;
    已有数据范围内查询会加间隙锁; 已有数据之外会加临键锁
    
  10. 唯一索引和普通索引会怎么影响锁?
    命中数据会加记录锁; 否则只会加间隙锁
    
  11. 你遇到过什么死锁问题吗?怎么排查的?最终又是怎么解决的?
    临键锁死锁,  select where id = ? (> 表中最大id) for update then insert lastid + 1, 2..., 临键锁都是 (表中最大id, max]
    
  12. 你有没有优化过锁?怎么优化的?
    像上面的例子中, 不加锁, 直接插入数据, 如果发生唯一索引或者主键冲突则执行数据存在逻辑, 否则执行数据不存在逻辑
    

13|MVCC协议:MySQL 修改数据时,还能不能读到这条数据?

  1. 什么是 MVCC?为什么需要 MVCC?
    多版本并发控制,  为了在提交读和可重复读隔离级别时, 提升读写并发
    
  2. 什么是隔离级别?隔离级别有哪几种?
    简单讲隔离级别就是一个事务是否了解别的事务以及程度怎么样.
    
  3. 什么是脏读、不可重复读、幻读?它们与隔离级别的关系是怎样的?
    脏读是读到其他事务未提交的更改;不可重复读是指事务中先后读取到的同一个数据不一致;幻读是指一个事务中, 因为有另一个事务的插入或更新数据从而读到了变更.
    
  4. 隔离级别是不是越高越好?
    隔离级别越高数据一致性越强, 并发行越差.
    
  5. 你们公司用的是什么隔离级别?为什么使用这个隔离级别?能不能使用别的隔离级别?
    使用默认可重复读隔离级别;可以降低为提交读.
    
  6. 你有没有改过隔离级别?为什么改?
    可重复读可能会因为临键锁而发生死锁, 可以通过改成提交读, 因为只有rr 级别里才有临键锁.
    

14|数据库事务:事务提交了,你的数据就一定不会丢吗?

  1. 什么是 Buffer Pool; 什么是日志缓冲区
    Buffer Pool 和 日志缓冲区是 InnoDB 的特性.
    Buffer Pool 是 InnoDB 的数据缓存区域,主要用于缓存数据页和索引页。
    日志缓冲区, 用于暂存 Redo Log,主要目的是支持事务的持久性和崩溃恢复。
    
  2. 什么是 undo log?为什么需要 undo log?
    回滚日志, mvcc 和事务回滚 需要
    
  3. 什么是 redo log?为什么需要 redo log?
    重放日志, 用于数据库崩溃恢复、持久化更改,系统崩溃数据不丢失、事务提交时顺序写提高性能
    
  4. 什么是 binlog?它有几种模式?用来做什么?
    二进制日志,3种模式:语句模式、行模式、混合模式;数据恢复、主从复制
    
  5. 事务是如何执行的?
    事务开启,
    执行查询,锁定
    写入undo log
    buffer pool 更新值
    写入redo log
    事务提交, 根据参数决定是否刷盘 redo log, 写入binlog
    刷新 buffer pool 到磁盘
    
  6. 什么是 ACID?隔离性和隔离级别是什么关系?你觉得哪个隔离级别满足这里的隔离性要 求?
    事务的原子性、一致性、隔离性、持久性
    innodb 可重复读解决了幻读, 所以可重复读和串行化都满足隔离性要求
    
  7. redo log 的刷盘时机有哪些?该如何选择?你们公司用的是哪个配置?为什么用这个配 置?
    innodb_flush_log_at_trx_commit
    0,1,2 分别是 每秒刷日志到磁盘、事务提交时刷日志到磁盘、提交给操作系统page cache 就不管了
    
  8. binlog 的刷盘时机有哪些?该如何选择?你们公司用的是哪个配置?为什么用这个配置?
    sync_binlog
    0 或 N, 分别代表写入 page cache 交给操作系统刷新 和 每 N 次提交事务(也会先写入到page cache) 刷新日志到磁盘
    
  9. 我的事务提交了,就一定不会丢吗?怎么确保一定不会丢?
    redo log 同步参数=0, 可能会丢1s 日志, =2 写到 page cache 需要等操作系统刷盘, 不确定性更大.
    binlog 同步参数=0, 也是写入到page cache, 同理. 如果!=1 则提交N 次才会刷盘. 如果期间崩溃 binlog 也会丢.
    
  10. 什么是 page cache?为什么不直接写到磁盘?
    操作系统缓存页. 写磁盘太慢
    
  11. 在分布式环境下,当服务器告诉我写入成功的时候,一定写入成功了吗?如果服务器宕机了可能发生什么?
    不一定, 取决于写入语义: 主节点写入、主节点子节点全部写入、主节点部分节点写入.
    服务器重数据:
    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|分库分表主键生成:如何设计一个主键生成算法?

  1. 你们分库分表怎么生成主键的?
    雪花算法
    
  2. 使用 UUID/数据库自增/雪花算法有什么优缺点?
    uuid 不是自增的; 数据库自增不是全局的; 雪花算法全局自增
    
  3. 雪花算法是如何实现的?
    一位保留+时间戳+机器id+序号
    
  4. 雪花算法是怎么做到全局唯一的?
    时间递增不同时间产生的id肯定是不同的、机器id是不同的, 同一时刻不同机器产生的id 肯定也不同、同一时刻同一机器id,可以轻易控制序号
    
  5. 怎么解决雪花算法的序列号耗尽问题?
    增加id 的长度,
    或者序号耗尽后, 等待至多1毫秒
    
  6. 怎么解决雪花算法的数据堆积问题?
    某一个时刻采用随机数作为起点, 作为序号产生的一部分
    
  7. 你有没有优化过主键生成的性能?怎么优化的?效果如何?
    singleflight 批量取:
    
  8. 你的主键生成的 ID 是严格递增的吗?不是递增有什么问题?
    严格递增, 不是递增的容易造成数据页分裂
    
  9. 为什么我们一般使用自增主键?
    唯一性保证:自增主键由数据库自动生成,保证每个记录的标识是唯一的,避免了手动指定主键可能导致的冲突问题。
    高效索引:自增主键通常是整数类型(如 INT 或 BIGINT),占用空间小且递增有序。这种特性使得在 B+树索引(常见于关系型数据库如 MySQL)中插入和查询效率更高,因为新记录总是追加到索引的末尾,减少了索引分裂和重组的开销。
    简单易用:自增主键无需开发者手动维护,数据库会自动为每条新记录分配一个递增的ID,简化了开发流程,降低了出错概率。
    
  10. 什么是页分裂?有什么缺点?
    数据库数据因为插入新的数据造成结构重构, 可能划分成多个容量的数据页, 可能造成数据缓存命中率下降
    

17|分库分表分页查询:为什么你的分页查询又慢又耗费内存?

关键观念: 全局查询、游标查询

18|分库分表事务:如何同时保证分库分表、ACID及高性能?

  1. 在分库分表之后,如何解决事务问题?
    分布式事务, 两阶段三阶段提交:适合对强一致性要求的场景,但性能较差。
    本地事务 + 消息队列:适合对最终一致性要求的高并发场景。
    TCC 和 Saga, 需要实现回滚:适合复杂业务场景,灵活性高,但开发成本较高。
    分布式事务中间件:适合需要快速实现分布式事务的场景。
    

21|数据库综合应用:怎么保证数据库高可用、高性能?

  1. InnoDB 引擎的 buffer pool 是拿来做什么的?怎么优化它的性能?
    缓存表和索引; 调整 innodb_buffer_pool_size (调整到 内存的 70% 或者 75%)和  innodb_buffer_pool_instances 参数
    
  2. 数据库里面有很多刷盘相关的参数,你都了解吗?调过吗?根据什么来调?
    innodb_flush_log_at_trx_commit: redo log 刷盘时机
    sync_binlog: bin log 刷盘时机