1、请你说说mysql索引,以及它们的好处和坏处
检索效率、存储资源、索引
- 索引就像指向表行的指针,是一个允许查询操作快速确定哪些行符合WHERE子句中的条件,并检索到这些行的其他列值的数据结构
- 索引主要有普通索引、唯一索引、主键索引、外键索引、全文索引、复合索引
- 在大数据量的查询中,合理使用索引的优点非常明显,不仅能大幅度提高匹配where条件的检索效率,还能用于排序和分组操作的加速
- 但是索引如果使用不当也有比较大的坏处:索引必定会增加存储资源的消耗;增大了插入、更新和删除操作的维护成本,因为每个增删改操作后相应的索引都必须被更新
只要创建了索引,就一定会走索引嘛?
不一定,比如,在使用组合索引的时候,如果没有遵从“最左前缀”的原则进行搜索,则索引是不起作用的。假设在id、name、age字段上已经成功建立了一个名为MultiIdx的组合索引。索引行中按id、name、age的顺序存放,索引可以搜索id、(id、name)、(id、name、age)字段组合。如果列不构成索引最左面的前缀,那么mysql不能使用局部索引,如age或(name,age)组合则不能使用该索引查询。
2、请你介绍一下数据库的ACID
原子性、一致性、隔离性、持久性
事务可由一条非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成。在事务中的操作,要么都执行修改,要么都不执行,这是事务的目的,也是事务模型区别于文件系统的重要特征之一。
A(atomicity)原子性
原子性指整个数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作都执行成功,整个事务的执行才算成功。事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。
C(consistency)一致性
一致性指事务将数据库从一种状态转变为另一种一致的状态。在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
I(isolation)隔离性
事务的隔离性要求每个读写事务的对象与其他事务的操作对象能相互分离,该事务提交前对其他事务都不可见,通过锁来实现。
D(durability)持久性
事务一旦提交,其结果就是永久性的,即使发送宕机等故障,数据库也能将数据恢复。持久性保证的是事务系统的高可靠性,而不是高可用性。
3、请你说说InnoDB的MVCC
无锁并发
- Multi-Version Concurrency Control,多版本并发控制。逻辑是维持一个数据的多个版本,使得读写操作没有冲突。
- MVCC主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。它是一种用来解决读-写冲突的无锁并发控制机制。
- 在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高数据库并发读写性能,还可以解决脏读、幻读、不可重复读等事务隔离问题,但不能解决更新丢失问题。
- 实现原理主要依赖记录中的3个隐式字段、undo日志、ReadView
- InnoDB默认的隔离级别是RR(REPEATABLE READ),RR解决脏读、不可重复读、幻读等问题,使用的是MVCC,它最大的优点是读不加锁,因此读写不冲突,并发性能好。
- InnoDB实现MVCC,多个版本的数据可以共存,主要基于以下技术及数据结构:
- 1、隐藏列:InnoDB中每行数据都有隐藏列,隐藏列中包含了本行数据的事务id、指向undo log的指针等;
- 基于undo log的版本链:每行数据的隐藏列中包含了指向undo log的指针,而每条undo log也会指向更早版本的undo log,从而形成一条版本链。
- 3、ReadView:通过隐藏列和版本链,mysql可以将数据恢复到指定版本。但是具体要恢复到哪个版本,则需要根据ReadView来确定。ReadView指事务(记作事务A)在某一时刻给整个事务系统(trx_sys)打快照,之后再进行读操作时,会将读取到的数据中的事务id与事务系统快照比较,从而判断数据对该ReadView是否可见,即对事务A是否可见。快照包含了当前哪些事务id是活跃的,之后读取时不能读这部分。
4、请你讲讲B树和B+树
- 他们都是平衡多路查找树,是在二叉查找树基础上改进的数据结构。在二叉查找树上查找一个数据时,最坏的查找次数为树的深度,当数据量很大时,查询次数可能还是很大,造成大量的磁盘IO,从而影响查询效率。为了减少磁盘IO的次数,必须降低树的深度,因此在二叉查找树基础上将树改成了多叉加上一些限制条件,就形成了B树
- B+树是B树的变种,区别主要是:对于k阶的B树,每个中间节点只存k-1个值和k个指针,而B+树存k个值和k个指针;B树中所有节点中的值的总集是全部关键字集合;而B+树中所有叶子节点值的总集就是全部关键字集合;B+树为所有叶子节点增加了链接,从而实现了快速的范围查找
- B+树由B树和索引顺序访问方法演化而来,它是为磁盘或其他直接存取辅助设备设计的一种平衡查找树,在B+树中,所有记录点都是按键值的大小顺序放在同一层的叶子节点,各叶子节点通过指针进行链接。
- B+树索引在数据库中的一个特点就是高扇出性,例如在InnoDB存储引擎中,每个页的大小为16KB。在数据库中,B+树的高度一般在2-4层,这意味着查找某一键值最多只需2-4次IO操作。一般磁盘每秒至少可以做100次IO操作,2-4次IO操作意味着查询时间只需0.02-0.04秒。
5、请你讲讲缓存穿透、击穿、雪崩的区别
1、缓存穿透
- 缓存穿透是指客户端查询了根本不存在的数据,使得这个请求直达存储层,导致其负载过大甚至造成宕机。这种情况可能是由于业务层误将缓存和库中的数据删除造成的,当然也不排除有人恶意攻击,专门访问库中不存在的数据导致缓存穿透。
- 通过缓存空对象的方式和布隆过滤器两种方式来解决
- 缓存空对象是指当存储层未命中后,仍然将空值存入缓存层,当客户端再次访问数据时,缓存层直接返回空值
- 还可以将数据存入布隆过滤器,访问缓存之前以过滤器拦截,若请求的数据不存在则直接返回空值
2、缓存击穿
- 原因:当一份访问量非常大的热点数据缓存失效的瞬间,大量的请求直达存储层,导致服务崩溃。
- 解决方案:
- 1、通过热点数据不设置过期时间来解决,这样就不会出现上述问题,“物理”上永不过期。
- 2、或为每个数据设置逻辑过期时间,当发现该数据逻辑过期时,使用单独的线程重建缓存;
- 3、还可以通过加互斥锁的方式来解决缓存击穿,对数据的访问加互斥锁,当一个线程访问该数据时,其他线程只能等待。这个线程访问过后,缓存中的数据将被重建,届时其他线程可以直接从缓存中取值。
3、缓存雪崩
- 指当某一时刻缓存层无法继续提高服务,导致所有的请求直达存储层,造成数据库宕机。可能是缓存中有大量数据同时过期,也可能是Redis节点发生故障,导致大量请求无法得到处理;
- 解决方案
- 1、设置过期时间时,附加一个随机数,避免大量的key同时过期
- 2、启用降级和熔断措施,发生雪崩时,若应用访问的不是核心数据,则直接返回预定义信息/空值/错误信息。或者在发生雪崩时,对于访问缓存接口的请求,客户端并不会把请求发给Redis,而是直接返回。
- 3、构建高可用的Redis服务,采用哨兵或集群模式,部署多个Redis实例,这样即使个别节点宕机,依然可以保持服务的整体可用。
6、数据库为什么不用红黑树而用B+树
磁盘IO
- 红黑树是一种近似平衡二叉树(不完全平衡),结点非黑即红的树,它的树高最高不会超过2logn,查找时间复杂度o(logn),无论增删改查,它的性能都十分稳定;但是红黑树本质还是二叉树,在数据量非常大时,需要访问+判断的结点还是会比较多,同时数据是存在磁盘上的,访问需要进行磁盘IO,导致效率较低
- B+树是多叉的,可以有效减少磁盘IO次数;B+树增加了叶子结点间的连接,能保证范围查询时找到起点和终点后快速取出需要的数据
- 红黑树做索引底层数据的缺陷:以红黑树作为底层数据结构在面对这些表数据动辄数百万数千万的场景时,创建的索引它的树高得有多高?索引从根结点开始查找,而如果我们需要查找的数据在底层的叶子结点上,那么树的高度是多少就要进行多少次查找,数据存在磁盘上,访问需要进行磁盘IO,这会导致效率过低
- 红黑树作为索引数据结构的弊端:树的高度过高导致查询效率变慢
7、请你说说乐观锁和悲观锁
1、乐观锁
- 乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新时判断在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。
- 乐观锁适用于多读的应用类型,可提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。
2、悲观锁
- 悲观锁总是假设最坏的情况,每次去拿数据都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程)。
- 传统的关系型数据库里用到了很多这种锁机制,比如行锁,表锁,读锁,写锁等,都是在操作之前先上锁。在并发量不是很大,且并发情况导致异常用户和系统都很难接受的情况下,会选择悲观锁。
8、请你说说聚簇索引和非聚簇索引
索引即数据,二次查询;数据和索引是否分离
- 聚簇索引是将数据与索引存储在一起,找到索引也就找到了数据
- 非聚簇索引是将数据和索引存储分离开,索引树的叶子结点存储了数据行的地址。
- InnoDB中,一个表有且仅有一个聚簇索引,因为原始数据只留一份,而数据和聚簇索引在一起,并且该索引是建立在主键上的,即使没有指定主键,也会特殊处理生成一个聚簇索引;其他索引都是辅助索引,使用辅助索引访问索引外的其他字段时都需要进行二次查找
- InnoDB中,可将B+树索引分为聚簇索引和辅助索引(非聚簇索引),无论哪种索引,每个页的大小都为16KB,且不能更改。聚簇索引是根据主键创建的一颗B+树,聚簇索引的叶子结点存放了表中的所有记录。辅助索引是根据索引键创建的一颗B+树,与聚簇索引不同的是,其叶子结点存放索引键值,以及该索引键值指向的主键。如果通过辅助索引来查找数据,那么当找到辅助索引的叶子节点后,很有可能还需要根据主键值查找聚簇索引来得到数据,这种查找方式称为书签查找。因为辅助索引不包含行记录的所有数据,意味着每页可以存放更多的键值,其高度一般都小于聚簇索引。
- MyISAM中,所有索引都是非聚簇索引,叶子结点存储着数据地址,对主键索引和普通索引在存储上没有区别。
9、请你说说Redis如何与数据库保持双写一致性
4种同步策略及其可能出现的问题,重试机制
4种同步策略
- 先更新缓存再更新数据库
- 先更新数据库再更新缓存
- 先删除缓存再更新数据库
- 先更新数据库再删除缓存
先更新缓存:
- 优点:每次数据变化时都能及时地更新缓存,这样不容易出现查询未命中的情况
- 缺点:这种操作消耗很大,如果数据需要经过复杂的计算再写入缓存的话,频繁的更新缓存会影响到服务器的性能。如果都是写入数据比较频繁的场景,可能会导致频繁的更新缓存却没有业务来读取该数据
删除缓存
- 优点:操作简单,无论更新的操作复杂与否,都是直接删除缓存中的数据
- 缺点:当删除缓存之后,下一次查询容易出现未命中的情况,需再次读取数据库
- 对比而言,删除缓存较先更新缓存是更好的选择
先操作数据和后操作数据的区别
- 先删除缓存再操作数据库的话,如果第2步失败可能导致缓存和数据库得到相同的旧数据
- 先操作数据库但删除缓存失败的话会导致缓存和数据库得到的结果不一致
- 出现上述问题,一般采用重试机制解决,为避免重试机制影响主要业务的执行,一般建议重试机制采用异步的方式执行
- 当采用重试机制之后由于存在并发、先删除缓存依然可能存在缓存中存储了旧的数据,而数据库中存储了新的数据,二者数据不一致的情况
先更新数据库,再删除缓存是影响更新的方案。如果第2步失败,采用重试机制解决问题。
10、请你说说Innodb和MyISAM的区别
事务、锁、读写性能、存储结构
- InnoDB是具有事务、回滚和崩溃修复能力的事务安全型引擎,可实现行级锁的大量数据中的并发操作
- MyISAM是具有默认支持全文索引、压缩功能及较高查询性能的非事务性引擎
- 事务:InnoDB支持事务,MyISAM不支持
- 数据锁:InnoDB支持行级锁,MyISAM只支持表级锁
- 读写性能:InnoDB增删改性能更优;MyISAM查询性能更优
- 全文索引:InnoDB不支持(但可通过插件等方式支持),MyISAM默认支持
- 外键:InnoDB支持外键,MyISAM不支持
- 存储结构:InnoDB在磁盘存储为一个文件;MyISAM在磁盘上存储3个文件(表定义、数据、索引)
- 存储空间:InnoDB需要更多的内存和存储;MyISAM支持3种存储格式:静态表(默认)、动态表、压缩表
- 移植:InnoDB在数据量小时可通过拷贝数据文件、备份binlog、mysqldump工具移植,数据量大时比较麻烦;可单独对某个表通过拷贝表文件移植
- 崩溃恢复:InnoDB有崩溃恢复机制;MyISAM没有
- 默认推荐:InnoDB是MySQL5.5之后的默认引擎
InnoDB中行级锁是怎么实现的?
- 通过给索引上的索引项加锁来实现。只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。当表中锁定其中的某几行时,不同的事务可以使用不同的索引锁定不同的行。不论使用主键索引、唯一索引还是普通索引,InnoDB都会使用行锁来对数据加锁。
11、请你说说数据库引擎有哪些,各自有什么区别
InnoDB
- InnoDB引擎是Mysql的事务安全(ACID兼容)存储引擎,具有提交、回滚和崩溃恢复功能来保持用户数据;行级锁定读取增加了多用户并发性和性能;将用户数据存储在聚集索引中,以减少基于主键的常见查询I/O;还支持外键维护数据完整性
MyISAM
- MyISAM引擎的表占用空间较小,表级锁定限制了读/写工作负载的性能,常用于只读或以读取为主的场景
Memory
- Memory引擎是将所有数据存储在RAM中,以便在需要快速查找非关键数据的环境中进行快速访问,以前被称为HEAP引擎。
12、请你说说数据库索引的底层数据结构
B+树
索引可选的底层数据结构包括:
- 二叉树:在某些场景下会退化成链表,而链表的查找需要从头部开始遍历,这就失去了加索引的意义
- 红黑树
- hash
- B-tree
13、请你说说数据库索引是什么结构,为什么不用哈希表
B+树、内存资源
- MySQL中索引B+树实现的
- 哈希表的查询效率最高,o(1),但它要求将所有数据载入内存,而数据库存储的数据量级可能会非常大,全部载入内存基本上是不可能实现的。无法做范围查询,只支持精确查询;查询时间不稳定,哈希冲突时,最坏o(n)
- B+树可以分段加载需要的节点数据,可以在内存资源有限的前提下,极大提高查询效率
14、请你说说mysql的事务隔离级别
未提交读、已提交读、可重复读、可串行化
15、请你说说mysql主从同步是如何实现的
**复制(replication)**是mysql数据库提供的一种高可用高性能的解决方案,一般用于建立大型的应用。
- 1、主服务器(master)把数据更改记录到二进制日志(binlog)中
- 2、从服务器(slave)把主服务器的二进制日志复制到自己的中继日志(relay log)中
- 3、从服务器重做中继日志中的日志,把更新应用到自己的数据库上,以达到数据的最终一致性
完全备份+二进制日志备份的还原
二进制日志的还原操作基本上实时在进行中。复制不是完全实时地进行同步,而是异步实时。存在主从服务器之间地执行延时,如果主服务器地压力很大,则可能导致主从服务器延时较大。
从服务器有2个线程:
- I/O线程,负责读取主服务器的二进制日志,并将其保存为中继日志
- SQL线程,复制执行中继日志