参考
- 《高性能MySQL(第三版)》
- CyC2018/CS-Notes
- Snailclimb/JavaGuide
数据库系统原理
- 什么是事务?
事务是满足原子性、一致性、隔离性、持久性的一组操作,可以通过Commit提交一个事务,也可以使用Rollback回滚。 - 解释一下事务的四大特性。
– 原子性:事务不可分割,要么全部执行, 要么失败回滚
– 一致性:多个事务对同一个数据的读取结果是相同的,不能存在事务读取到旧数据
– 隔离性:一个事务所做的修改对其他事务是不可见的
– 持久性:事务提交之后,能够持久保存在数据库中,不丢失。 - 四大特性之间的关系是怎样的?
数据库需要满足一致性和持久性,一致性需要靠原子性和隔离性来保证。 - 数据库在并发情况下,会产生哪些问题?
在并发条件下如果没有保证隔离性会出现这些问题:
– 修改丢失:另一个事务的修改覆盖了当前的修改。
– 脏读:一个事务读取到了另一个事务的中间数据
– 不可重复读:由于另一个事务的修改导致当前事务对数据的多次读取结果不同。
– 幻影读:当前事务统计数据时,另一个事务插入或删除数据,导致多次统计结果不同。 - 事务有哪些隔离级别?
– 未提交读:没有隔离,会造成脏读、不可重复读、幻影读
– 提交读:其他事务提交后再读取,解决脏读问题
– 可重复读:对同一数据多次读取结果一致,解决不可重复读问题
– 可串行化:事务串行执行,不会存在并发一致性问题。 - InnoDB的隔离级别怎样,如何解决并发一致性问题?
InnoDB的隔离级别是可重复读,使用多版本并发控制解决可重复读问题,使用Next-Key lock解决幻影读问题。 - 介绍一下3种范式。
高一级的范式包括所有低级范式的规则
– 第一范式:属性不可分。表中每个字段不可以继续分解。
– 第二范式:非主属性完全依赖于键码。字段必须依赖于所有主键
– 第三范式:非主属性禁止传递函数依赖。可以继续分解。
MySQL
1. 基础内容
- 一条SQL命令的执行流程是怎样的?
客户端发送携带sql命令的请求,服务器进行语法解析和查询优化后,条用存储引擎的接口,存储引擎执行具体的查询操作,读取数据后返回。 - MyISAM与InnoDB引擎有什么区别?
MyISAM是MySQL5.5之前的默认引擎,提供了数据压缩,执行速度快,但不支持事务,只有表级锁,且崩溃后无法安全恢复。
InnoDB是MySQL5.5之后的默认引擎,支持事务、行级锁、外键,支持热备份。 - MySQL的数据类型有哪些?
整数:tinyint,smallint,mediumint,int,bigint
浮点数:float,double,decimal
字符串:char,varchar(变长)
时间日期:DateTime,TimeStamp
整数和浮点数按照需要进行选择,字符串一般选择varchar,日期时间一般选择TimeStamp
2. 索引
- 谈一下B+Tree的原理。
b+树图文详解
– 数据保存在叶子节点,非叶子节点只保存每个子节点的最大值,这样每次查询都需要达到叶子节点,性能稳定
– 叶子节点是有序的,并且在叶子节点使用指针连接形成链表,适合范围查询。 - MySQL的索引结构为什么使用B+树,而不是其他树形结构?
https://www.bilibili.com/read/cv5985933/
– 二叉查找树:不平衡,可能退化为链表
– 平衡二叉树:需要维护平衡状态,需要旋转操作,在删除数据时的旋转需要O(logn)的时间。
– 红黑树:不要求严格平衡,引入了颜色,在插入和删除数据时只需要O(1)的时间进行旋转即可;然而树太高,IO次数多,查询效率有所下降
– B树:将二叉树改为多路平衡查找树,解决了树过高的问题
– B+树:在B树的基础上,将非叶子节点改造为不存储数据的纯索引节点,进一步降低高度,同时将叶子节点用链表连起来,范围查询更加高效。 - MySQL有哪些索引类型?
– B+Tree索引:是InnoDB的默认索引类型。InnoDB的B+Tree索引分为主索引和辅助索引,主索引的叶子节点记录了完整的数据,称为聚簇索引,表中的记录就是通过聚簇索引组织的,辅助索引的叶子节点记录了主键的值,因此使用辅助索引时,先查到主键的值,然后通过主索引查到具体的数据。支持全值查找、范围查找和最左前缀查找
– 哈希索引:主要用于全值的精确查找
– 全文索引:查找文本中的关键词 - 使用索引与表的大小的关系?
对于比较小的表,可以直接使用全表扫描;
对于中到大型的表,使用索引就很有效;
对于特大型的表,可以使用分区技术。 - 什么时候使用索引?
– 在where子句中出现的列,可以考虑使用索引
– 数据唯一性差的字段不要使用索引,因为这样构造出来的B+树,很多很多关键字相等,在非叶子节点需要进行很多次的比较 - 应该怎样正确设置使用索引?
– 索引列是独立的,不能带函数或者是表达式的一部分
– 如果查询条件是多个列,那么可以使用多列的复合索引,其中区分度更高的列应该放在前面
– 索引覆盖查询,如果索引包含了需要查询的值,那么就不需要再从主索引中读取记录。 - 什么时候索引会失效?
– where子句中对索引列使用函数或表达式;
– 查询条件中有or,那么需要or左右的列都需要索引,否则不会使用索引;
– 存在隐式类型转换的,不会使用索引;
– 范围查询中以%开头;
– 复合索引中,如果不使用最左列,那么后续列也无法使用索引;
– mysql可能会对语句进行优化,优化后的语句可能不使用索引更快。
3. 优化
-
如何优化查询?
– 使用explain:查看select语句的查询类型,使用的索引,扫描的行数等
– 减少请求的数据量:只获取需要的列,使用limit获取需要的行
– 切分大查询,大的查询可能一次锁住很多数据,消耗资源
– 分解多表关联查询:将多表关联查询分解为单表查询,然后在应用层连接。这样可以让缓存更容易命中,减少数据库压力 -
如何进行大表优化?
可以对表进行切分,包括水平切分和垂直切分
– 水平切分:又称为Sharding,将记录拆分到多个结构相同的表中
– 垂直切分:可以将经常使用的列和不经常使用的列切分到不同的表中 -
如何进行水平切分?
哈希取模hash(key)%N;按照Id范围划分;自定义切分然后把记录和分表的映射关系储存到一张映射表中。 -
水平切分产生什么问题,怎么解决?
– 事务问题:InnoDB支持分布式事务
– 多表关联:拆分成单表查询,然后在应用程序层关联
– ID唯一性:全局ID,为每个分表指定一个ID范围,分布式id生成器 -
如何排查慢查询?
– 在config中开启慢查询日志
– 使用mysqldumpslow工具查看慢sql
– 使用show processlist查看线程运行状态
– 使用explain查看select语句的执行
Redis
- Redis为什么速度快?
– 大部分操作是基于内存的
– 使用单线程,避免了多线程竞争和上下文切换开销。
– 使用非阻塞IO - 介绍一下Redis单线程的工作模型。
redis 单线程的理解
首先多个Redis客户端是通过多个线程与Redis服务端建立socket连接,然后Redis对这些连接进行探测,查找出已经接收完客户端请求的连接,然后采用单线程的方式对请求数据进行处理,最后返回数据。 - Redis有哪些数据类型?
String、Hash、List、Set、SortedSet - SortedSet的底层实现?
少量数据使用ziplist,大量数据使用skiplist。skiplist是跳跃表,方便有序链表的搜索,相比于红黑树,实现简单,插入迅速。每个节点不仅有指向当前层下一个节点的指针,还有指向下一层元素的指针,查找时先从上层开始查找,这样可以跳过很多元素。 - Redis清除过期的key有哪些方式?
– 定期删除:默认每100ms,随机抽取一些设置了过期时间的key,检验其是否过期
– 惰性删除:查询时,如果key过期,则触发删除 - Redis有哪些key淘汰策略?
如果redis内存耗尽时,需要触发key的淘汰策略。
两个范围所有key,设置了过期时间的key。3种策略:LRU、LFU、RANDOM。组合得到6种,另外还有不淘汰,淘汰最快要过期的。一共8种。 - 谈一下Redis的持久化机制。
Redis有RDB快照和AOF文件两种持久化方案。RDB快照是每隔一段时间进行一次快照,文件较小。AOF记录每个写操作到AOF文件,类似于mysql的binlog。 - 什么是缓存雪崩、缓存穿透、缓存击穿,解决方案是什么?
– 缓存雪崩:同一时刻大量缓存失效,导致请求全部进入数据库,导致数据库大量负载而崩掉。正确设置数据过期时间,如随机过期时间,热点数据永不过期等。
– 缓存穿透:服务器接收到无效请求,缓存和数据库都没有数据,导致请求不断进入数据库,从而引起数据库压力过大。可以做参数校验拦截,也可以在缓存中放入值为空的键值对。
– 缓存击穿:如果一个热点数据过期,那么会有很多并发请求进入数据库,引起数据库压力瞬间增大。可以设置热点数据永不过期。