赞
踩
Redis面试总结,自己学习使用。
Redis是一个使用C语言编写的,高性能非关系型的键值对数据库,与传统的数据库不同的是,Redis的数据是存在内存中的,所以读写速度非常快,被广泛应用于缓存方向,Redis可以将数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。默认端口号是6379
优点:
缺点:
为什么是单线程?
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的。
Redis支持多线程主要有两个原因:
基本数据类型:
特殊的数据类型:
Bitmaps:位图,可以认为是一个以位为单位数组,数组中的每个单元只能存0或者1,数组的下标在Bitmap 中叫做偏移量。Bitmap的长度与集合中元素个数无关,而是与基数的上限有关。可以用来统计网站中活跃的人数。
HyperLogLog。HyperLogLog 是用来做基数统计的算法,其优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。典型的使用场景是统计独立访客。
Geospatial :主要用于存储地理位置信息,并对存储的信息进行操作,适用场景如定位、附近的人等。
Redis的单线程的。keys指令会导致线程阻塞一段时间,直到执行完毕,服务才能恢复。scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是 O(1) ,但是要真正实现keys的功能,需要执行多次scan。
scan的缺点:在scan的过程中如果有键的变化(增加、删除、修改),遍历过程可能会有以下问题:新增的键可能没有遍历到,遍历出了重复的键等情况,也就是说scan并不能保证完整的遍历出来所有的键。
相同点:
不同点:
Redis的单条命令是保证原子性的,但是事务是不保证原子性的。
Redis事务的本质:一组命令的集合,一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行,一次性、顺序性、排他性,执行这些命令。
Redis事务没有隔离级别的概念,也就是没有幻读,脏读这些概念。
事物的生命周期:
悲观锁:认为什么时候都会出问题,无论做什么都会加锁。
乐观锁:认为什么时候都不会出错,所以不会上锁,更新数据的时候去判断一下,在此期间是否有人修改过这个数据。version
WATCH 命令可以监控一个或多个键,一旦其中有一个键被修改,之后的事务就不会执行(类似于乐观锁)。执行 EXEC 命令之后,就会自动取消监控。
Redis提供了两种持久化方式,分别是RDB持久化和AOF持久化。
RDB持久化:RDB持久化会在指定的时间间隔内将内存中的数据快照存储到硬盘上,这个快照文件就是RDB文件。RDB文件是一个二进制文件,它包含了Redis在某个时间点上的所有数据。当需要恢复数据时,Redis会读取RDB文件,并根据文件中的内容来还原数据库。RDB持久化的优点是备份恢复数据速度快,适合用于备份和灾难恢复。缺点是如果在Redis发生故障时,最后一次持久化的数据可能会丢失。
AOF持久化:AOF持久化会将Redis的所有写操作追加到一个日志文件中,这个日志文件就是AOF文件。当Redis需要恢复数据时,它会读取AOF文件,并重新执行文件中保存的写操作,来还原数据库。AOF持久化的优点是可以保证更高的数据安全性,因为它记录的是每个写操作,即使Redis在持久化时发生故障,也可以通过AOF文件来还原数据。缺点是相比于RDB持久化,AOF持久化的性能较低。
在实际应用中,可以根据业务场景和数据安全要求选择不同的持久化方式,或者将两种持久化方式结合使用。例如,可以使用AOF持久化来保证数据安全性,再使用RDB持久化来定期备份数据。
持久化就是把内存的数据写到磁盘中,防止服务宕机导致内存数据丢失。
Redis支持两种方式的持久化,一种是 RDB 的方式,一种是 AOF 的方式。前者会根据指定的规则定时将内存中的数据存储在硬盘上,而后者在每次执行完命令后将命令记录下来。一般将两者结合使用。
Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以Redis提供了持久化功能。
RDB 是 Redis 默认的持久化方案。RDB持久化时会将内存中的数据写入到磁盘中,在指定目录下生成一个 dump.rdb 文件。Redis 重启会加载 dump.rdb 文件恢复数据。
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的 Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)ー个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替換上次持久化好的文件。整个过程中,主进程是不进行任何I/O操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是RDB,一般情況下不需要修改这个配置。
触发机制:
优势:
劣势:
AOF(append only file)持久化:以独立日志的方式记录每次写命令,Redis重启时会重新执行AOF文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性,AOF 是Redis持久化的主流方式。
默认情况下Redis没有开启AOF方式的持久化,可以通过 appendonly 参数启用: appendonly yes 。开启AOF方式持久化后每执行一条写命令,Redis就会将该命令写进 aof_buf 缓冲区,AOF缓冲区根据对应的策略向硬盘做同步操作。
默认情况下系统每30秒会执行一次同步操作。为了防止缓冲区数据丢失,可以在Redis写入AOF文件后主动要求系统将缓冲区数据同步到硬盘上。可以通过 appendfsync 参数设置同步的时机。
appendfsync always //每次写入aof文件都会执行同步,最安全最慢,不建议配置
appendfsync everysec //既保证性能也保证安全,建议配置
appendfsync no //由操作系统决定何时进行同步操作
如果AOF文件大于64M,太大了,fork一个新的进程来将我们的文件进行重写。
AOF 持久化执行流程:
RDB的优点:
体积更小:相同的数据量rdb数据比aof的小,因为rdb是紧凑型文件
恢复更快:因为rdb是数据的快照,基本上就是数据的复制,不用重新读取再写入内存
性能更高:父进程在保存rdb时候只需要fork一个子进程,无需父进程的进行其他io操作,也保证了服务器的性能。
缺点:
故障丢失:因为rdb是全量的,我们一般是使用shell脚本实现30分钟或者1小时或者每天对redis进行rdb备份,(注,也可以是用自带的策略),但是最少也要5分钟进行一次的备份,所以当服务死掉后,最少也要丢失5分钟的数据。
耐久性差:相对aof的异步策略来说,因为rdb的复制是全量的,即使是fork的子进程来进行备份,当数据量很大的时候对磁盘的消耗也是不可忽视的,尤其在访问量很高的时候,fork的时间也会延长,导致cpu吃紧,耐久性相对较差。
AOF的优点:
数据保证:我们可以设置fsync策略,一般默认是everysec,也可以设置每次写入追加,所以即使服务死掉了,咱们也最多丢失一秒数据
自动缩小:当aof文件大小到达一定程度的时候,后台会自动的去执行aof重写,此过程不会影响主进程,重写完成后,新的写入将会写到新的aof中,旧的就会被删除掉。但是此条如果拿出来对比rdb的话还是没有必要算成优点,只是官网显示成优点而已。
缺点:和rdb相反,毕竟只有两种。
性能相对较差:它的操作模式决定了它会对redis的性能有所损耗
体积相对更大:尽管是将aof文件重写了,但是毕竟是操作过程和操作结果仍然有很大的差别,体积也毋庸置疑的更大。
恢复速度更慢:
通常来说,应该同时使用两种持久化方案,以保证数据安全。
当RDB与AOF两种方式都开启时,Redis会优先使用AOF恢复数据,因为AOF保存的文件比RDB文件更完整。
Redis发布订阅是一种消息通信模式,发送者(pub)发送消息,订阅者(sub)接收消息,应用于微信公众号,微博,关注系统
Redis客户端可以订阅任意数量的频道。
原理:
主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。
客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。
工作原理:
概念:
缓存穿透的概念很简单,用户想要查询一个数据,发现Redis内存数据库中没有,也就是缓存没有命中,于是想持久层数据库查询。发现也没有,于是本次查询失败,当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库,这就会给持久层数据库造成很大的压力,这时候就相当于是缓存穿透。
解决方案:
缓存击穿:大量的请求同时查询一个 key 时,此时这个 key 正好失效了,就会导致大量的请求都落到数据库。缓存击穿是查询缓存中失效的 key,而缓存穿透是查询不存在的 key。
解决方法:
缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到数据库,数据库瞬时压力过重挂掉。
解决方案:
问题描述:
随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力,为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题。
分布式锁主流的实现方案:
Redis分布式锁机制,主要借助setnx和expire两个命令完成。
SETNX命令:SETNX 是SET If Not Exists的简写。将 key 的值设为 value,当且仅当 key 不存在; 若给定的 key 已经存在,则 SETNX 不做任何动作。
127.0.0.1:6379> set lock "unlock"
OK
127.0.0.1:6379> setnx lock "unlock"
(integer) 0
127.0.0.1:6379> setnx lock "lock"
(integer) 0
127.0.0.1:6379>
expire命令:expire命令为 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除. 其格式为:
127.0.0.1:6379> expire lock 10
(integer) 1
127.0.0.1:6379> ttl lock
8
以上简单redis分布式锁的问题:
如果出现了这么一个问题:如果setnx是成功的,但是expire设置失败,一旦出现了释放锁失败,或者没有手工释放,那么这个锁永远被占用,其他线程永远也抢不到锁。
所以,需要保障setnx和expire两个操作的原子性,要么全部执行,要么全部不执行,二者不能分开。
解决的办法有两种:
方法一:简单加锁:使用set的命令时,同时设置过期时间使用set的命令时,同时设置过期时间的示例如下:
127.0.0.1:6379> set users "234" nx ex 10
OK
127.0.0.1:6379> ttl users //查看过期时间
Redis 官方站提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock,此种方式比原先的单节点的方法更安全。它可以保证以下特性:
Redis中的Zset(有序集合)是一种基于跳表(Skip List)实现的数据结构。跳表是一种基于链表的数据结构,它允许快速查找、插入和删除操作,同时还保证了元素的有序性。Zset作为一种有序集合,需要保证元素的有序性和快速查找。
在Zset中,每个元素都有一个权重值(score),元素按照权重值从小到大排序,相同权重的元素按照字典序排序。Zset需要支持添加、删除元素,以及根据权重值范围查找元素、计算元素排名等操作。使用跳表可以快速定位元素的位置,支持快速查找和修改,同时也保证了元素的有序性。
跳表在维护有序性的同时,还具有较高的插入、删除效率,其时间复杂度为O(log n),比平衡二叉树的时间复杂度O(log n)要小,而且实现起来比较简单,所以跳表是实现Zset的一种较好的选择。
总的来说,Redis中的Zset使用跳表实现可以提供快速的元素查找、插入和删除操作,同时保证元素的有序性,是一种性能和效率较好的数据结构。
见参考
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。