每日八股:MySQL与Redis
MySQL
MYSQL引擎
1.InnoDB:MySQL默认存储引擎,具有ACID事务支持、行级锁、外键约束等特性,适用于高并发的读写操作,支持较好的数据完整性和并发操作。
2.MyiSAM:MySQL的另一个存储引擎,不支持事务、行级锁和外键约束,适用于大量读操作的场景,在并发写入和数据完整性方面有一定的限制。
3.Memory:将事务存储在内存中,适用于对性能要求较高的读操作,可能会造成数据丢失,不支持事务、行级锁和外键约束。
MySQL为什么InnoDB是默认引擎
因为InnoDB在事务支持、并发性能、崩溃恢复等方面具有优势。
mysql的innodb与MyISAM的区别?
1.事务:innodb支持事务,MyISAM不支持事务。
2.索引结构:innodb是聚簇索引,MyISAM是非聚簇索引。
3.锁粒度:innodb的最小锁粒度是行锁,MyISAM的最小锁粒度是表锁。
4.count的效率:innodb不保存表的具体行数,执行count函数需要扫描整个表,MyISAM保存表的具体行数,执行速度更快。
Redis
Redis的底层数据结构
1.String:可以是字符串、整数或浮点数。
2.List:链表,链表上的每个节点都包含一个字符串。
3.Set:无序集合
4.Hash:无序散列表
5.Zset:有序集合
Zset底层是怎么实现的?
Zset底层数据结构通过压缩列表或跳表实现。 如果Zset的元素个数少于128个,每个元素的值小于64个字节时,使用压缩列表,否则使用跳表。
介绍一下listpack
listpack相较于压缩列表最大的特点是每个节点中不再存储上一个节点的长度,压缩列表中正是因为每个节点包含了前一个节点的长度信息,导致存在连锁更新的隐患。listpack还是用一块连续的内存空间来紧凑的保存数据,并且为了节省内存的开销,listpack节点会采用不同的编码方式来存储不同大小的节点数据。
Redis中哈希表是怎么进行扩容的?
Redis采用渐进式rehash。在rehash时,会用到两个哈希表(哈希表1和哈希表2),步骤如下:
1.给哈希表2分配空间
2.在rehash期间,每次哈希表元素进行增删改查等操作时,Redis除了执行对应的操作外,还会顺序将哈希表1中索引位置上的所有键值对信息转移到哈希表2中
3.随着处理客户端发起的哈希表操作请求数量越多,最终在某个时间点会将哈希表1中的全部键值对信息都迁移到哈希表2中,从而完成rehash操作。
哈希表扩容的时候,有读请求怎么查?
在哈希表扩容的时候,查找一个key值的话,会先去哈希表1中进行查找,如果没有找到,再去哈希表2中进行查找。
Redis为什么快?
1.Redis大部分操作都是在内存中完成的,并且采用了高效的数据结构,因此Redis的瓶颈可能是内存或者网络带宽,而不是cpu,既然cpu不是瓶颈,那就自然采用单线程的解决方案了。
2.Redis采用单线程模型可以避免多线程之间的竞争,省去了多线程切换带来的时间和性能上的开销,同时也不会导致死锁问题。
3.Redis采用了I/O多路复用机制来处理大量的客户端socket请求。
如何实现redis原子性?
1.redis执行一条命令的时候是具备原子性的,因为redis是单线程执行的,不存在多线程安全的问题。如果要保证两条命令的原子性,可以考虑使用Lua脚本,将多个操作写到一个Lua脚本中,redis会把整个Lua脚本当成一个整体来执行,执行过程中不会被其他命令打断,从而保证了Lua脚本中操作的原子性。
2.redis中事务也能保证原子性。若redis事务正常执行,可以保证原子性;redis事务执行过程中某一个操作执行失败,不保证原子性。
Redis的持久化方式
1.AOF日志:每执行一条写操作命令,就将该命令以追加的方式写入到一个文件里。
优点:提供了更好的安全性,因为它默认将每一次写操作都追加到文件末尾,即使redis发生了故障,也只会丢失最后一次写入前的数据。
缺点:因为记录了每一个写操作,所以AOF占用的磁盘空间大。当redis发生故障之后进行重写时,若AOF文件体积过大,可能会对redis写入操作性能造成一定的影响,因为它需要重放所有的写操作。
2.RDB快照:将某一时刻的内存数据,以二进制的方式写入磁盘。
优点:RDB通过快照的形式保存某一时刻的数据状态,文件体积小,备份和恢复的速度快,不会对redis的性能造成很大的影响。
缺点:RDB方式在两次快照之间,如果redis服务器发生了故障,那么这段时间内的数据将全部丢失。并且,如果在RDB创建快照到恢复期间有写操作,恢复后的数据可能与故障前的数据不一致。
过期删除策略和内存淘汰策略的区别
1.内存淘汰策略是指内存要满了的时候,redis回触发内存淘汰策略,淘汰一些不必要的内存资源,以腾出空间,保存新的内容。
其中内存淘汰策略可分为不进行数据淘汰的策略和进行数据淘汰的策略。
不进行数据淘汰的策略:表示当运行内存超过最大设置内存时,不淘汰任何数据,这时如果有数据写入,会报错禁止数据写入,对于单纯的查询或删除操作可以正常执行。
进行数据淘汰的策略:可细分为在设置了过期时间的数据中进行淘汰和在所有数据范围内进行淘汰。
2.过期删除策略是指将已过期的键值对信息进行删除,redis中采用的过期删除策略是惰性删除+定期删除。
Redis的缓存失效会不会立即删除?
不会,Redis的过期删除策略是采用惰性删除+定期删除这两种策略配合使用的。
惰性删除策略的做法是:不会主动删除过期键,每次从数据库访问key时,都会检查key是否过期,如果过期则删除。
定期删除策略的做法是:每隔一段时间都会随机的从数据库中抽取一定数量的key,检查它们是否过期,删除其中已过期的key。
redis主从和集群可以保证数据一致性吗?
redis主从和集群都属于AP模型,即在面临网络分区时选择保证可用性和分区容忍性,而牺牲了强一致性。这意味着在网络分区的情况下,redis主从复制和集群可以继续提供服务并保持可用,但可能会出现部分节点之间的数据不一致。
哨兵机制说一下
哨兵机制:它的作用是实现主从节点故障转移。它会监测主节点是否存活,如果发现主节点挂了,它会立即选举一个从节点切换为主节点,并且把新主节点的信息传递给从节点和客户端。哨兵节点主要负责三件事:监控、选主、通知。
哨兵机制的选主节点的算法
1.故障节点主观下线
2.故障节点客观下线
3.Sentinel集群选举Leader
4.Sentinel Leader决定新主节点
Redis集群模式以及优缺点
当Redis缓存数据量大到一台服务器无法缓存时,就需要使用Redis分片集群方案,它将数据分布在不同的服务器上,降低了系统对单主节点的依赖,从而提高了redis的读写性能。
优点:高可用性、高性能、扩展性好。
缺点:部署和维护较复杂、集群同步问题、数据分片限制。
本地缓存和Redis缓存的区别?
1.本地缓存是指将数据存储在本地应用程序或服务器上,通常用于加速数据访问和提高响应速度,本地缓存使用内存作为存储介质,利用内存高速读写的特性来提高数据访问速度。
优点:访问速度快、减轻网络压力、低延迟。
缺点:可扩展性有限。
2.Redis缓存是指将数据存储在多个分布式节点上,通过协同工作来提供高性能的数据访问服务。分布式存储通常使用集群方式进行部署,利用多台服务器来分担数据访问和存储的压力。
优点:可扩展性强、数据一致性高、易于维护。
缺点:访问速度慢、网络开销大。
Redis的应用场景
缓存、分布式锁、排行榜、计数器、消息队列。
Redis的大Key问题是什么?
redis的大Key问题指的是某个key对应的value值在内存中占用空间过大,导致redis的性能下降、内存不足、数据不均衡以及主从同步延迟等问题。
大Key问题的缺点
内存占用过高、性能下降、阻塞其他操作、网络拥塞、主从同步延迟、数据倾斜。
Redis中大Key如何解决?
1.对大Key进行拆分
2.对大Key进行清理
3.监控Redis的内存水位
4.对过期数据进行定期清理
什么是热Key?
通常以其接收到的Key被请求频率来判定:
1.QPS集中在特定的Key
2.带宽使用率集中在特定的Key
3.CPU使用时间占比集中在特定的Key
如何解决热Key问题?
1.在Redis集群架构中对热Key进行复制
2.使用读写分离架构
缓存雪崩、击穿、穿透是什么?怎么解决?
1.缓存雪崩:当大量缓存数据在同一时间过期(失效)或Redis故障宕机时,如果此时有大量的用户请求,都无法在Redis中处理,于是全部请求直接访问数据库,就会使数据库的压力骤增,严重的会导致数据库宕机,从而引发一系列的连锁反应,造成系统崩溃。
解决方案:
①均匀设置过期时间:如果要给缓存数据加上一个过期时间,应避免大量数据设置成同一个过期时间。我们可以在对缓存数据设置过期时间时,给这些数据的过期时间加上一个随机数,这样就保证数据不会在同一时间过期。
②互斥锁:当业务线程在处理用户请求时,如果发现访问的数据不在redis里,就加一个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库中读取数据,再将数据更新到redis),当构建缓存完成后,再释放锁。
③后台更新缓存:业务线程不再负责更新缓存,也不为缓存设置有效期,而是让缓存永久有效,将更新缓存的工作交给后台线程定时更新。
2.缓存击穿:如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,都无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮。
解决方案:
①互斥锁方案:保证同一时间内只有一个业务线程更新缓存,未获取到互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或默认值。
②不给热点数据设置过期时间,由后台异步更新缓存,或在热点数据即将过期时,提前通知后台线程更新缓存以及重新设置过期时间。
3.缓存穿透:当用户访问的数据,即不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失数据,再去访问数据库时,发现数据库中也没有要访问的数据,无法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增。
解决方案:
①非法请求的限制:当有大量恶意的请求访问不存在的数据时,也会发生缓存穿透,因此在API的入口处应判断请求参数是否合理,请求参数是否含有非法值,请求字段是否存在,如果发现是恶意请求就直接返回错误,避免进一步访问缓存和数据库。
②缓存空值或默认值:当我们线上业务发现缓存穿透现象时,可以针对查询的数据,在缓存中设置一个空值或默认值,这样后续请求就可以从缓存中获取到空值或默认值返回给应用,而不会继续查询数据库。
③布隆过滤器:我们可以在写入数据时,使用布隆过滤器做个标记,当业务线程确认缓存失效后,可以通过布隆过滤器判断数据是否存在,如果不存在,就不用通过数据库来判断数据是否存在。即使发生了缓存穿透,大量请求也只会访问redis和布隆过滤器,而不会查询数据库,保证了数据库的正常运行。
布隆过滤器的原理
布隆过滤器由初始值都为0的位图数组和N个哈希函数构成。当我们在写入数据库数据时,在布隆过滤器里做个标记,这样下次在查询数据是否在数据库中时,只需要查询布隆过滤器,如果数据没有被标记,说明数据不在数据库中。布隆过滤器会通过下面三个步骤完成:
1.使用N个哈希函数分别对数据进行哈希运算,得到N个哈希值
2.将第一步得到的N个哈希值对位图数组的长度取模,得到每个哈希值在位图数组中的对应位置
3.将每个哈希值在位图数组中对应位置的值都设为1
当应用要查询数据是否在数据库中时,通过布隆过滤器查询位图数组中对应位置的值是否全为1,只要有一个为0,说明该数据不在数据库中。布隆过滤器由于是基于哈希函数实现的,高效查找的同时会存在哈希冲突的可能性,可能会存在多个数据处于位图数组的同一位置的情况从而导致误判。所以,查询布隆过滤器说数据存在,并不一定数据就存在于数据库中,但若查询数据不存在,则数据库中一定不存在这个数据。
#八股##面试常问题系列##Java选手#