对象面试官系列之数据库--面试官看了都说好

1 事务的特性:ACID

原子性:mysql通过回滚日志(undo log)实现,回滚日志有回滚需要的逻辑信息

一致性:数据库在事务执行前后都保持一致性状态

隔离性:通过锁实现

持久性:事务提交后,则其所做的修改将会永远保存到数据库中不会丢失;在对内存操作的同时也会将当前数据页(页面报头;数据行;行偏移矩阵:记录的逻辑顺序,比如按照聚集索引的顺序)修改后的值写入重做日志(redo log)中,然后再将数据写入磁盘,有错误再根据日志恢复数据

redo/undo log、binlog区别

Undo记录某数据被修改前的sql语句,Redo记录当前数据页面被修改后的值;

先在内存操作数据,然后写入redo日志缓存中,数据写入磁盘,然后写入binlog日志,事务提交;

2 并发一致性问题

丢失修改(同时提交修改数据),脏读(事务回滚),不可重复读(更新),幻读(新增,删除)

隔离级别:未提交读,提交读(每次查询都创建readview),可重复读,串行化

MYSQL如何实现可重读:

1.多版本并发控制,MVCC在每行记录后面都保存一个存储创建版本号和指向undo log日志的回滚指针还有一个删除标志位和自增id。

更新操作时,会先在undo log日志记录更新之前的数据,然后新增一行记录创建版本号,回滚指针指向undo log记录的数据。

第一次读取数据时,会生成一个ReadView,记录了当前活跃的事务版本号,查询时,如何数据没有被删除,先判断数据的创建版本号是否小于活跃的最小事务版本号或者不属于活跃版本号,如果不是,通过回滚指针不断进行判断;如何数据被删除,只会返回大于当前事务版本号的数据

2.innodb解决幻读(next-Key lock+MVCC)

普通读(快照读)幻读:MVCC+回滚日志

当前读幻读(for update;lock in share mode):next-Key lock:

索引等值查询,且索引命中:主键、唯一索引在索引项及其主键索引加排他锁;二级索引除索引项及其主键索引加排他锁外,在各索引项间加GAP锁;无索引加表锁

索引等值查询,且索引未命中:主键、唯一索引会在包含记录的两个索引项间加GAP锁;二级索引会在包含记录的两个索引间加GAP锁并在左侧索引项上加X锁

索引范围查询:范围内索引项加X锁外,并在索引项间加GAP锁

缺点:会引起死锁;因为间隙锁之间是兼容的所以会出现两个事务对同一个区间加同样的锁,那么两个事务都无法进行数据的新增

3 数据库三大范式

第一范式:属性不应该是可分的。

第二范式:每一列都和主键相关,而不能只与主键的某一部分相关

第三范式:非主键之间不能有相关性

4 数据库锁

4.1 锁的类型

1.排他锁(写锁)和共享锁(读锁)

加了共享锁锁就可以进行读不能进行更新操作,加锁期间其它事务能读锁但不能加写锁。加了排它锁,就可以进行读取和更新,加锁期间其它事务不能加任何锁。

2.意向共享锁,意向排它锁

一个事务在获得某个数据行对象的共享锁之前,必须先获得表的意向

共享锁;一个事务在获得某个数据行对象的排他锁之前,必须先获得表的意向排它锁。

3.悲观锁乐观锁

悲观锁:排它锁共享锁

乐观锁:版本号

4.2 三级封锁协议

一级:修改数据时必须加排他锁,事务结束释放,解决丢失修改

二级:一级基础上读取数据时必须加读锁,读取完释放,解决脏读

三级:二级基础上读取数据加读锁,事务结束释放,解决不可重复读

5 索引

5.1 索引的数据结构

有序数组:插入数据成本较高,适合数据表一旦建立后不再会修改

哈希表:不是有序的,所以只适合做等值查询

位图:适合列只有几个固定值

搜索树:B树、B-树、B+树

B树、B-树、B+树区别:

B树:1.所有非叶子结点至多拥有两个儿子,所有结点存储一个关键字;

2.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树

B-树:1.m阶树每个节点最多有m个子节点

2.根节点至少有两个节点

3.非根节点和叶节点数[m/2,m];

4.每个节点的关键字个数为[m/2-1,m-1],指针为关键字数-1

B+树:1.指针和关键字个数一样

2.叶子节点之间有指针进行关联,所有关键字都在叶子结点出现

5.2 索引原理

索引很大所以一般放在磁盘当中,因为磁盘的机械结构导致磁盘IO的成本很高,所以我们选取的数据结构应该尽量减小IO的次数。计算机有一个磁盘预读机制,在每一次磁盘IO时,不仅会读取当前的数据,还会读取附近的数据,读取的长度为一页。搜索树索引会将节点设置为一页的大小,这样每个节点只需要一次IO,而B+数相比其他搜索树的高度更低,所以需要的磁盘IO次数也最少,所以选用B+树。

扩展:

一、索引的优点

数据库在磁盘中以数据页的形式储存(页面报头;数据行;行偏移矩阵:记录的逻辑顺序,比如按照聚集索引的顺序),通过双向链表进行关联,页中的记录是按照单链表存储,如果不走索引需要遍历双向链表和单链表找到数据,时间复杂度高

二、索引缺点

创建索引和维护索引需要耗费许多时间

索引占用物理存储空间

三、B+比B更好的地方

1.IO次数少:B+树的中间结点只存放索引,数据都存在叶结点中,因此中间结点可以存更多的数据,让索引树更加矮胖;

2.范围查询效率更高:B树需要中序遍历整个树,B+树只需要遍历叶结点中的链表;

3.b树极端情况下会退化成链表,B+树不会

四、B+比B-好的地方

1.中间节点只存放索引,可以实现一个节点就一次IO

2.b+树叶子节点含有所有数据,区间查询效率高

5.3 索引分类

1.普通索引(非聚集索引):

2.唯一索引(非聚集索引):索引列的值必须唯一;

3.主键索引(聚集索引):不允许空值;MySQL创建主键时默认为聚集索引,但主键也可以是非聚集索引);

4.全文索引(非聚集索引):关键词匹配

聚集索引和非聚集索引区别:

1.聚集索引在叶子节点存储的是表中所有的数据,非聚集索引在叶子节点存储的是主键的值和索引列的值(只能直接查到主索引和非聚集索引的值)

2.使用非聚集索引查询出数据时,拿到叶子上的主键再去聚集索引查到想要查找的数据。(拿到主键再查找这个过程叫做回表)

5.4 复合索引

最左原则

1.索引排序是根据从左向右匹配的,遇到范围查询停止匹配。

3.查询的时候,优化器会优化sql语句为对应索引的顺序,即:(a,c,b)也是可以用索引的,c > 3 and b = 2 and a = 1 and d

4.单独查询b,会采用index类型索引,即:不符合最左,直接查询所有的节点,找到对应的数据指针,回表,这样很慢。

5.5 适合创建索引的地方

1.某列经常作为最大最小值;

2.经常被查询的字段;

3.经常用作表连接的字段;

4.经常出现在ORDER BY/GROUP BY/DISDINCT后面的字段

5.6 索引失效

1.以“%(表示任意0个或多个字符)”开头的LIKE语句;

2.OR语句前后没有同时使用索引;

3.数据类型出现隐式转化(如varchar不加单引号的话可能会自动转换为int型);

4.对于多列索引,必须满足最左匹配原则/最左前缀原则

5.如果MySQL估计全表扫描比索引快,则不使用索引(比如非常小的表)

6 Innodb和myisam区别

1.Innodb支持行级锁和外键还有事务,myisam不支持行级锁和外键还有事务。

2.InnoDB的B+树主键索引的叶子节点就是数据文件,辅助索引的叶子节点是主键的值;而MyISAM的B+树主键索引和辅助索引的叶子节点都是数据文件的地址指针

3.innodb表中,没有缓存这个表的数量;而myisam缓存了。

7 优化数据库

7.1 优化SQL

1.将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联;

2.分页查询优化,先在子查询中获取主键索引然后进行关联查询获取需要的数据

7.2 优化索引

注意索引失效的条件和适合建索引的地方

7.3 表结构优化

1.三大范式,表的水平切分,表的垂直切分(可以将不常用的字段单独放在同一个表中;把大字段独立放入一个表中)

2.在线分库分表

3.增删改时,对两个库都进行双写,然后获取老库数据并对新库数据进行更新,可以加一个字段判断数据最新的修改时间;最后做一轮校验,比较两个库的所有数据

7.4 如何查找

1.慢查询日志

2.explain命令

8 Redis数据结构

默认端口:6379

1.string:单一数值,验证码

2.list:最新列表,关注列表

3.set:点赞点踩,已读,共同好友

4.map:对象属性不定

5.zset:排行榜

扩展

1.zset底层实现:有序集合保存的元素数量小于128个,有序集合保存的所有元素的长度小于64字节使用压缩表

2.压缩表:单层双向链表,按score排序

3.跳跃表和字典:多层有序链表实现,插入节点时会为每一个节点随机出一个层数,然后把他同时插入到多层的链表中,随机出3,插入1-3层

9 Redis持久化机制

9.1 保存redis的写命令(AOF)

1.所有的写入命令会追加到aof_buf缓冲区中。

2.AOF缓冲区根据对应的策略向硬盘的AOF文件写入数据

3.随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

4.当服务器重启时,可以加载AOF文件进行数据恢复。(先写后记录是为了避免写入无效命令)

1.AOF把命令追加到缓冲区的原因:

Redis可以提供多种缓冲区同步硬盘策略,在性能和安全性方面做出平衡:

always:缓冲区的内容写入内存缓冲区后马上同步到磁盘的AOF文件

everysec:AOF文件同步操作由子线程每秒调用一次。是建议的策略,也是默认配置,兼顾性能和数据安全。

no:AOF文件同步硬盘操作由操作系统负责,周期通常最长30秒。

2.AOF文件大如何解决:

aof有一个命令会开辟一个子线程对aof文件进行重做

缺点:AOF文件比RDB文件大很多

9.2 保存数据库的键值对(RDB)

Save:阻塞当前Redis服务器,直到RDB过程完成为止,

Bgsave:1.执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回。

2.父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞。

3.父进程fork完成后,bgsave命令返回并不再阻塞父进程,客户端的save和bgsave命令会被拒绝。

4.子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。

5.进程发送信号给父进程表示完成,父进程更新统计信息。

缺点:会丢失间隔期间的数据;需要开辟一个子线程,数据量大的话比较耗时,造成服务器短暂宕机

10 缓存血崩、缓存击穿和缓存穿透、热点key

10.1 缓存血崩

解释:缓存同一时间大面积的失效,请求都会访问数据库

解决:事前:尽量保证整个redis集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。

事中:本地ehcache缓存+ hystrix限流&降级,避免MySQL崩掉

事后:利用redis持久化机制保存的数据尽快恢复缓存

10.2 缓存击穿

解释:缓存中key过期,请求都会访问数据库

解决:1.热点key永不过期

2.查询到缓存不存在数据时加一个互斥锁,然后访问数据库更新缓存

10.3 缓存穿透

解释:缓存和数据库都不存在该数据,请求都会访问数据库

解决:缓存无效key:如果缓存和数据库都查不到某个key的数据就写一个到redis,值设置为null并设置过期时间

布隆过滤器:将所有可能存在的数据的哈希值存到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉

10.4 热点key问题

解决:1.二级缓存:加一个本地缓存(hashmap),发现热点key后走本地缓存

2.备份热点key:多个redis服务器上保存热点key

11 redis快的原因

1.redis是基于内存的,内存的读写速度非常快

2.它是单线程,没有进程竞争,锁等设置,所以少了切换上下文的时间,相对快了很多。

3.redis使用io多路复用(epoll)

在多路复用IO模型中,会有一个线程(Java中的Selector)不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。因为在多路复用IO模型中,只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有socket读写事件进行时,才会使用IO资源,所以它大大减少了资源占用。

12 redis过期策略

Redis会用一个字典保存过期时间,键为redis键值对的指针,值为过期时间

定时过期(设置一个定时器,key过期就立马删除);惰性过期(访问到key就删除);定期过期(每隔一段时间从过期哈希表中随机删除过期key)

Redis使用惰性过期+定期过期

13 redis内存淘汰策略

Redis的INFO–Redis命令可以查看当前内存使用情况

1.volatile-lru:已设置过期时间的数据集中挑选最近最少使用的数据淘汰

2.volatile-ttl:已设置过期时间的数据集中挑选将要过期的数据淘汰

3.volatile-random:从已设置过期时间的数据集中任意选择数据淘汰

4.allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)

5.allkeys-random:从数据集中任意选择数据淘汰

6.no-eviction:当内存使用达到阈值的时候,再申请内存会抛出异常(默认)

实现机制:

1.LRU:redis为每个对象维护了一个时钟,还有一个定时更新的全局计数器,当新增或者更新时,会把全局计数器的值赋值给对象时钟;然后lru时会默认随机挑选5个key值,然后淘汰对象时钟和全局时钟差值最大的key

2.TTL:从存储时间过期表随机挑选几个键值对然后选择快过期的数据进行淘汰

3.随机淘汰:随机选择键值对进行淘汰

14 redis分布式锁

加锁直接使用set命令同时设置唯一id和过期时间,然后如果key值存在的话就不加锁;其中解锁稍微复杂些,加锁之后可以返回唯一id,标志此锁是该客户端锁拥有;释放锁时要先判断拥有者是否是自己,然后删除,这个需要redis的lua脚本保证判断和删除两个命令的原子性执行

15 redis主从同步

主负责写,从负责读,刚连接时全量同步,后面增量同步

全量同步:从服务器连接主服务器后,主服务器会执行bgsave命令生成快照文件,同时会用内存缓存区记录写命令,然后会向从服务器发送快照文件和缓存区命令,从服务器会丢弃以前的数据,载入快照文件(对于从服务器是非阻塞的,查询会查询以前的数据),然后会执行写命令;

增量同步:主服务器执行一次写命令就向从服务器发送相同的命令

哨兵模式

多个哨兵会监控若干个主服务器和从服务器以及互相监控,哨兵通过定期的ping命令判断主服务器是否下线,然后会向监控这个主服务器的其他节点(哨兵会向主服务器发送自己的集群信息,然后哨兵会订阅这个频道)发送命令询问他们的检测结果,超过一定数量检测为下线就判断已下线;然后哨兵之间会通过彼此发送命令投票选择负责主从切换的哨兵,然后哨兵选出新的主节点,并告诉其他从节点。

#高频知识点汇总##春招##秋招##Java##学习路径#
全部评论
🎉恭喜牛友成功参与 【创作激励计划】高频知识点汇总专场,并通过审核! ------------------- 创作激励计划5大主题专场等你来写,最高可领取500元京东卡和500元实物奖品! 👉快来参加吧:https://www.nowcoder.com/discuss/804743
点赞 回复
分享
发布于 2021-12-23 21:12

相关推荐

12 49 评论
分享
牛客网
牛客企业服务