什么是 Redis?
Redis 完全免费开源,遵守[BSD协议],性能极高nosql提供多种语言的数据库 API非关系数据库。
- Redis优势:
- Redis本质上是基于内存的高性能key-value通过异步操作,期将数据库数据保存到硬盘中 。
- Redis性能极高:速度快,因为数据存在于内存中,读取速度可以达到1.1万次/s,8.1万次/s 。
- Redis 支持rdb,aof数据持久化的方法有两种,可以在磁盘中保存内存中的数据,重启时可以重新加载使用。
- Redis备份支持数据,即master-slave数据备份模式。
- 丰富的数据类型:五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合),单个value最大限制是512M。还有丰富的特点:Redis还支持publish/subscribe, 通知, key 过期等特点
- Redis也可以存入key设置过期时间,过期后自动删除。
- Redis支持事务,是原子操作,所谓的原子是数据变更要么全部执行,要么全部不执行 。
- Redis主要缺点是数据库容量受物理内存的限制,不能用作海量数据的高性能读写。Redis适用于小数据量的高性能操作和操作。
Redis 与其他 key - value 缓存产品有三个特点:
-
Redis 支持数据的持久性,可以在磁盘中保存内存中的数据,重启时可以重新启动 次加载进行使用。
-
Redis 不仅支持简单 key-value 同时提供类型数据 list,set,zset, hash 存储等数据结构。
-
Redis 备份支持数据,即 master-slave 数据备份模式。
-
Redis 与其他 key-value 存储有什么区别? (1)Redis 这是一种不同于其他数据库的进化路径,具有更复杂的数据结构,并为其提供原子操作。Redis 基于基本数据结构的数据类型对程序员透明,无需额外抽象。 (2)Redis 由于数据量不能大于硬件内存,因此在高速读写不同数据集时需要权衡内存,但可以持续到磁盘中。内存数据库的另一个优点是,与磁盘上相同复杂的数据结构相比,内存操作非常简单 Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面,它们是以额外的方式紧凑产生的,因为它们不需要随机访问。
Redis 相比 Memcached 有哪些优势?
(1)Memcached 所有值都是简单的字符串,redis 作为替代品,支持更丰富的数据类 (2)Redis 的速度比 Memcached 快 (3)Redis 数据可以持久
是否使用过Redis集群的原理是什么?
Redis Sentinal着眼于高可用性,在master宕机时会自动将slave提升为master,继续提供服务。 Redis Cluster注重扩展,注重单个redis使用内存不足时Cluster分片存储。
Pipeline有什么好处,为什么要用?pipeline?
可以将多次IO前提是往返时间一次,前提是pipeline执行指令之间没有因果关系。使用redis-benchmark压力测量可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数量。在此,我推荐一个架构学习交流圈。交流学习指导伪欣:1253431195(有大量的面试问题和答案)将分享一些资深架构师录制的视频:Spring,MyBatis,Netty高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等已成为架构师必备的知识体系。还可以获得免费的学习资源,目前受益匪浅
说说 Redis 哈希槽的概念?
答:Redis 集群没有使用一致性 hash,引入哈希槽的概念,Redis 集群有16384 每个哈希槽 key 通过 CRC16 校验后对 16384 为了决定放置哪个槽,集群的每个节点负责一部分 hash 槽。
redis的事务回滚.
redis事务不支持回滚.
Redis 是单进程单线程吗?
redis 将并发访问转换为串行访问,消除了传统数据库串行控制的费用。
什么是Redis持久化?
持久性是将内存数据写入磁盘,防止服务停机时内存数据丢失。
为什么redis所有数据都需要放在内存中
Redis通过异步将数据写入磁盘,以达到最快的读写速度。redis它具有快速和持久的数据特征。如果不在内存中放置数据,磁盘I/O严重影响速度redis的性能。 若设置了最大使用的内存,如果数据达到存储上限,则无法继续存储新数据。
Redis持久的方法有哪些?优缺点是什么?
Redis提供两种持久的方法:RDB(默认) 和AOF 持久性是将内存数据写入磁盘,防止服务停机时内存数据丢失。 redis可根据不同的配置持久,redis以二进制的方式将数据快照存储在硬盘中,默认文件名称为dump.rdb.或者可以手动调用save命令生成快照。
-
1、RDB:rdb是Redis DataBase缩写 RDB是Redis默认的持久性方法。是指在一定时间点将数据写入临时文件,以快照的形式将内存数据保存到硬盘中,并以快照的形式生成相应的数据文件dump.rdb。持久化结束后,用此临时文件替换上次持久化文件,实现数据恢复。
-
只有一份文件 dump.rdb,方便持久。
-
2.容灾性好,文件可保存在安全磁盘中。
-
三、性能最大化,fork 子过程完成写作操作,使主过程继续处理命令 IO 最大化。使用单独子过程进行持久化,主过程不会进行任何 IO 操作,保证 redis 的高性能
-
4.与数据集相比,比较 AOF 启动效率更高。
-
1.数据安全性低。RDB 如果持久化之间持续一段时间, redis 如果出现故障,数据将丢失。因此,这种方法更适合当数据要求不严格时)
-
2、AOF(Append-only file)持久: 指所有命令行的记录 redis 命令请 求协议格式完全持久存储)保存 aof 文件。
-
2、AOF (Append-only file)持久: 指所有命令 redis 命令请求协议的格式完全持久地存储在 aof 文件中。 AOF持久化(即Append Only File持久化),则是将Redis每次执行的命令都记录在单独的日志文件中,当重启时Redis将恢复持久日志中文件的数据。 当两种方法同时打开时,数据恢复Redis会优先选择AOF恢复。
-
1、数据安全,aof 可配置持久性 appendfsync 属性,有 always,每进行一次 记录命令操作 aof 一次文件。
-
2、通过 append 即使服务器在中途停机,也可以通过模式写文件 redis-check-aof 解决数据一致性问题的工具。
-
3、AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大时会对命令 合并重写)可以删除一些命令(如误操作) flushall)) 执行服务器(定时)任务或函数时flushAppendOnlyFile 调用函数, 该函数执行以下两个工作 WRITE:根据条件,将 aof_buf 写入缓存 AOF 文件 SAVE:根据条件调用 fsync 或 fdatasync 函数,将 AOF 将文件保存到磁盘中。 (1)AOF 文件比 RDB 文件大,恢复速度慢。 (2)数据集大时,比较 rdb 启动效率低。
AOF文件是一个只进行追加的日志文件。 Redis 可以在AOF文件体积变得过大时。自动地在后台对AOF进行重写。 AOF文件有序的保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此AOF文件的内容非常容易被人读懂,对文件进行分析也很轻松。 对于相同的数据集来说,AOF文件的体积通常大于RBD文件的体积。 根据所使用的的fsync策略,AOF的速度可能慢于RDB。
redis持久化的几种方式
- 1、快照(snapshots)(RDB)
- 2、AOF(Append Only File)持久化功能 AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
- 当redis重启时会优先载入AOF文件来恢复原始的数据,在通常情况下AOF文件保存的数据集要比RDB文件保存的数据要完整.
优缺点是什么?
1、AOF文件比RDB更新频率高,优先使用AOF还原数据。 2、AOF比RDB更安全也更大 3、RDB性能比AOF好 4、如果两个都配了优先加载AOF
使用过Redis分布式锁么,它是什么回事?
先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。
- 接着问如果在setnx之后执行expire之前进程意外crash或者要重启维护了,那会怎么样? 如果在 setnx 之后,执行 expire 之前进程意外 crash 或重启维护, 那么就需要把 setnx 和 expire 合成一条指令来用
如果有大量的key需要设置同一时间过期,一般需要注意什么?
如果大量的key过期时间设置的过于集中,到过期的那个时间点,redis可能会出现短暂的卡顿现象。一般需要在时间上加一个随机值,使得过期时间分散一些。
Redis常见性能问题和解决方案:
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件 (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次 (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内 (4) 尽量避免在压力很大的主库上增加从库 (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3… 这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
Redis的并发竞争问题如何解决?
Redis为单进程单线程模式,采用队列模式将并发访问变为串行访问。Redis本身没有锁的概念,Redis对于多个客户端连接并不存在竞争,但是在Jedis客户端对Redis进行并发访问时会发生连接超时、数据转换错误、阻塞、客户端关闭连接等问题,这些问题均是由于客户端连接混乱造成。对此有2种解决方法: 1.客户端角度:为保证每个客户端间正常有序与Redis进行通信,对连接进行池化,同时对客户端读写Redis操作采用内部锁synchronized。 2.服务器角度:利用setnx实现锁。
使用过Redis做异步队列么,你是怎么用的?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。 如果对方追问可不可以不用sleep呢?list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。 如果对方追问能不能生产一次消费多次呢?使用pub/sub主题订阅者模式,可以实现1:N的消息队列。 如果对方追问pub/sub有什么缺点?在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。 如果对方追问redis如何实现延时队列?我估计现在你很想把面试官一棒打死如果你手上有一根棒球棍的话,怎么问的这么详细。但是你很克制,然后神态自若的回答道:使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。 到这里,面试官暗地里已经对你竖起了大拇指。但是他不知道的是此刻你却竖起了中指,在椅子背后。
Redis支持的数据类型?
常用命令: set,get,decr,incr,mget 等。 String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 string类型是Redis最基本的数据类型,一个键最大能存储512MB。 常规key-value缓存应用; 常规计数:微博数,粉丝数等。
常用命令: hget,hset,hgetall 等。 Hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以Hash数据结构来存储用户信息,商品信息等等。比如下面我就用 hash 类型存放了我本人的一些信息:
key=JavaUser293847
value={
“id”: 1,
“name”: “SnailClimb”,
“age”: 22,
“location”: “Wuhan, Hubei”
}
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边) 格式: lpush name value 在 key 对应 list 的头部添加字符串元素 格式: rpush name value 在 key 对应 list 的尾部添加字符串元素 格式: lrem name index key 对应 list 中删除 count 个和 value 相同的元素 格式: llen name 返回 key 对应 list 的长度
格式: sadd name value Redis的Set是string类型的无序集合。 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
格式: zadd name score value Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。 zset的成员是唯一的,但分数(score)却可以重复。
Redis 的数据类型?
Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表), set(集合)及 zsetsorted set:有序集合)。
我们实际项目中比较常用的是 string,hash 如果你是 Redis 中高级用户,还需要 加上下面几种数据结构 HyperLogLog、Geo、Pub/Sub。 如果你说还玩过 Redis Module,像 BloomFilter,RedisSearch,Redis-ML,面 试官得眼睛就开始发亮了。
Redis 的数据类型?
Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及 zsetsorted set:有序集合)。 我们实际项目中比较常用的是 string,hash 如果你是 Redis 中高级用户,还需要加上下面几种数据结构 HyperLogLog、Geo、Pub/Sub。 如果你说还玩过 Redis Module,像 BloomFilter,RedisSearch,Redis-ML,面试官得眼睛就开始发亮了。
- Redis Module:
- BloomFilter:
- RedisSearch:
- Redis-ML:
使用过Redis分布式锁么,它是怎么实现的?
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。 set指令有非常复杂的参数,这个应该是可以同时把setnx和expire合成一条指令来用的!
Redis事物的了解CAS(check-and-set 操作实现乐观锁 )?
和众多其它数据库一样,Redis作为NoSQL数据库也同样提供了事务机制。在Redis中,MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石。
WATCH命令和基于CAS的乐观锁:
在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。例如,我们再次假设Redis中并未提供incr命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:
val = GET mykey
val = val + 1
SET mykey $val
以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景–竞态争用(race condition)。比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助,见如下代码:
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。
使用过Redis做异步队列么,你是怎么用的?有什么缺点?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。 在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如rabbitmq等。 能不能生产一次消费多次呢? 使用pub/sub主题订阅者模式,可以实现1:N的消息队列。
Redis的缓存失效策略和主键失效机制
作为缓存系统都要定期清理无效数据,就需要一个主键失效和淘汰策略. 在Redis当中,有生存期的key被称为volatile。在创建缓存时,要为给定的key设置生存期,当key过期的时候(生存期为0),它可能会被删除。
- 1、影响生存时间的一些操作 生存时间可以通过使用 DEL 命令来删除整个 key 来移除,或者被 SET 和 GETSET 命令覆盖原来的数据,也就是说,修改key对应的value和使用另外相同的key和value来覆盖以后,当前数据的生存时间不同。 比如说,对一个 key 执行INCR命令,对一个列表进行LPUSH命令,或者对一个哈希表执行HSET命令,这类操作都不会修改 key 本身的生存时间。另一方面,如果使用RENAME对一个 key 进行改名,那么改名后的 key的生存时间和改名前一样。 RENAME命令的另一种可能是,尝试将一个带生存时间的 key 改名成另一个带生存时间的 another_key ,这时旧的 another_key (以及它的生存时间)会被删除,然后旧的 key 会改名为 another_key ,因此,新的 another_key 的生存时间也和原本的 key 一样。使用PERSIST命令可以在不删除 key 的情况下,移除 key 的生存时间,让 key 重新成为一个persistent key 。
- 2、如何更新生存时间 可以对一个已经带有生存时间的 key 执行EXPIRE命令,新指定的生存时间会取代旧的生存时间。过期时间的精度已经被控制在1ms之内,主键失效的时间复杂度是O(1), EXPIRE和TTL命令搭配使用,TTL可以查看key的当前生存时间。设置成功返回 1;当 key 不存在或者不能为 key 设置生存时间时,返回 0 。 最大缓存配置 在 redis 中,允许用户设置最大使用内存大小 server.maxmemory 默认为0,没有指定最大缓存,如果有新的数据添加,超过最大内存,则会使redis崩溃,所以一定要设置。redis 内存数据集大小上升到一定大小的时候,就会实行数据淘汰策略。
缓存雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
- 解决方案: 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。 如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。 设置热点数据永远不过期。
缓存穿透
用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
- 解决方案: 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截; 从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
-
解决方案: 设置热点数据永远不过期。 加互斥锁,互斥锁参考代码如下:
-
说明: 1)缓存中有数据,直接走上述代码13行后就返回结果了 2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。 3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。
什么是缓存雪崩
缓存雪崩我们可以简单的理解为:由于原有缓存失效,新缓存未到期间 (例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。 当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,会给后端系统带来很大压力。导致系统崩溃。
解决办法:
大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。
如何避免?
1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。 2:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期 3:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
什么是缓存穿透?
缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。每次都要去数据库再查询一遍,然后返回空,这样请求就绕过缓存直接查数据库,如果恶意请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
解决办法;
- 1.最常见的则是采用布,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
- 2.也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。
redis缓存击穿(热点Key)
缓存中的一个Key(比如一个促销商品),在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。 解决方案:对缓存查询加锁,如果KEY不存在,就加锁,然后查DB入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入DB查询
什么是缓存预热
缓存预热这个应该是一个比较常见的概念,相信很多小伙伴都应该可以很容易的理解,缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决思路:
1、直接写个缓存刷新页面,上线时手工操作下; 2、数据量不大,可以在项目启动的时候自动进行加载; 3、定时刷新缓存;
什么是缓存更新
除了缓存服务器自带的缓存失效策略之外(Redis默认的有6中策略可供选择),我们还可以根据具体的业务需求进行自定义的缓存淘汰,常见的策略有两种: (1)定时去清理过期的缓存; (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!具体用哪种方案,大家可以根据自己的应用场景来权衡。
什么是缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。 降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。 以参考日志级别设置预案: (1)一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级; (2)警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警; (3)错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级; (4)严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。 服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
缓存热点数据和缓存冷数据
热点数据,缓存才有价值 对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存,对于上面两个例子,寿星列表、导航信息都存在一个特点,就是信息修改频率不高,读取通常非常高的场景。 对于热点数据,比如我们的某IM产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。 ,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。
redis 过期键的删除策略?
- 1、定时删除:在设置键的过期时间的同时,创建一个定时器 timer). 让定时器在键 的过期时间来临时,立即执行对键的删除操作。
- 2、惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是 否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
- 3、定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至 于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
redis的过期策略以及内存淘汰机制
redis采用的是定期删除+惰性删除策略。 定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略. 定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。 于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。 不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。 在redis.conf中有一行配置 maxmemory-policy volatile-lru
Redis 的回收策略(提供 6种数据淘汰策略:)
- volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最 少使用的数据淘汰
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过 期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意 选择数据淘汰
- allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 .
- no-enviction(驱逐):禁止驱逐数据 注意这里的 6 种机制,volatile 和 allkeys 规定了是对已设置过期时间的数据集淘 汰数据还是从全部数据集淘汰数据,后面的 lru、ttl 以及 random 是三种不同的 淘汰策略,再加上一种 no-enviction 永不回收的策略。
- 1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率 低,则使用 allkeys-lru
- 2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用 allkeys-random ** 三种数据淘汰策略:** ttl和random比较容易理解,实现也会比较简单。主要是Lru最近最少使用淘汰策略,设计上会对key 按失效时间排序,然后取最先失效的key进行淘汰
1)redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。
(2)限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
(3)数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
Redis 为什么是单线程的
Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了(毕竟采用多线程会有很多麻烦!)Redis利用队列技术将并发访问变为串行访问 1)绝大部分请求是纯粹的内存操作(非常快速) 2)采用单线程,避免了不必要的上下文切换和竞争条件 3)非阻塞IO优点: 1.速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
- 支持丰富数据类型,支持string,list,set,sorted set,hash 3.支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
- 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除如何解决redis的并发竞争key问题
同时有多个子系统去set一个key。这个时候要注意什么呢? 不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。 (1)如果对这个key操作,不要求顺序: 准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可 (2)如果对这个key操作,要求顺序: 分布式锁+时间戳。 假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。 (3) 利用队列,将set方法变成串行访问也可以redis遇到高并发,如果保证读写key的一致性 对redis的操作都是具有原子性的,是线程安全的操作,你不用考虑并发问题,redis内部已经帮你处理好并发的问题了。
为什么 redis 需要把所有数据放到内存中?
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以 redis 具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘 I/O 速度为严重影响 redis 的性能。如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。
Redis 的同步机制了解么?
答:Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave,并同时将后续修改操作记录到内存 buffer,待完成后将 rdb 文件全量同步到复制节点,复制节点接受完成后将 rdb 镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
对于大量的请求怎么样处理
redis是一个单线程程序,也就说同一时刻它只能处理一个客户端请求; redis是通过IO多路复用(select,epoll, kqueue,依据不同的平台,采取不同的实现)来处理多个客户端请求的
怎么理解 Redis 事务?
(1)事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 (2)事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
Redis 如何做内存优化?
答:尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面。
Redis 回收进程如何工作的?
答:一个客户端运行了新的命令,添加了新的数据。Redis 检查内存使用情况,如果大于 maxmemory 的限制, 则根据设定好的策略进行回收。一个新的命令被执行,等等。所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地回收回到边界以下。如果一个命令的结果导致大量内存被使用(例如很大的集合的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。
Redis 的内存用完了会发生什么?
答:如果达到设置的上限,Redis 的写命令会返回错误信息(但是读命令还可以正常返回。)或者你可以将 Redis 当缓存来使用配置淘汰机制,当 Redis 达到内存上限时会冲刷掉旧的内容。
Redis 分布式锁是什么 ?
先拿 setnx 来争抢锁,抢到之后,再用 expire 给锁加一个过期时间防止锁忘记了释放。 如果在 setnx 之后,执行 expire 之前进程意外 crash 或重启维护, 那么就需要把 setnx 和 expire 合成一条指令来用。
Redis 常见性能问题和解决方案?
(1) Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件 (2) 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次 (3) 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内 (4) 尽量避免在压力很大的主库上增加从库 (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3…
为什么Redis的操作是原子性的,怎么保证原子性的?
对于Redis而言,命令的原子性指的是:一个操作的不可以再分,操作要么执行,要么不执行。 Redis的操作之所以是原子性的,是因为Redis是单线程的。 Redis本身提供的所有API都是原子操作,Redis中的事务其实是要保证批量操作的原子性。 多个命令在并发中也是原子性的吗? 不一定, 将get和set改成单命令操作,incr 。使用Redis的事务,或者使用Redis+Lua==的方式实现.
Redis事务
Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的 Redis会将一个事务中的所有命令序列化,然后按顺序执行。 1.redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。 2.如果在一个事务中的命令出现错误,那么所有的命令都不会执行; 3.如果在一个事务中出现运行错误,那么正确的命令会被执行。
1)MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。 2)EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。 当操作被打断时,返回空值 nil 。 3)通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。 4)WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
Redis实现分布式锁
Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系Redis中可以使用SETNX命令实现分布式锁。 将 key 的值设为 value ,当且仅当 key 不存在。 若给定的 key 已经存在,则 SETNX 不做任何动作
解锁:使用 del key 命令就能释放锁 解决死锁: 1)通过Redis中expire()给锁设定最大持有时间,如果超过,则Redis来帮我们释放锁。 2) 使用 setnx key “当前系统时间+锁持有的时间”和getset key “当前系统时间+锁持有的时间”组合的命令就可以实现。
Redis 常见性能问题和解决方案:
- 1、Master 最好不要写内存快照,如果 Master 写内存快照,save 命令调度 rdbSave 函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性 暂停服务
- 2、如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一
- 3、为了主从复制的速度和连接的稳定性,Master 和 Slave 最好在同一个局域网
- 4、尽量避免在压力很大的主库上增加从
- 5、主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…这样的结构方便解决单点故障问题,实现 Slave 对 Master 的替换。如果 Master 挂了,可以立刻启用 Slave1 做 Master,其他不变。
为什么 Redis需要把所有数据放到内存中?
Redis 为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数 据写入磁盘。所以 redis 具有快速和数据持久化的特征。如果不将数据放在内存中, 磁盘 I/O 速度为严重影响 redis 的性能。在内存越来越便宜的今天,redis 将会越 来越受欢迎。如果设置了最大使用的内存,则数据已有记录数达到内存限值后不 能继续插入新值。
Redis 的同步机制了解么?
Redis 可以使用主从同步,从从同步。第一次同步时,主节点做一次 bgsave, 并同时将后续修改操作记录到内存 buffer,待完成后将 rdb 文件全量同步到复制 节点,复制节点接受完成后将 rdb 镜像加载到内存。加载完成后,再通知主节点 将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。
Pipeline 有什么好处,为什么要用 pipeline?
可以将多次 IO 往返的时间缩减为一次,前提是 pipeline 执行的指令之间没有 因果相关性。使用 redis-benchmark 进行压测的时候可以发现影响 redis 的 QPS 峰值的一个重要因素是 pipeline 批次指令的数目。
是否使用过 Redis 集群,集群的原理是什么?
1)、Redis Sentinal 着眼于高可用,在 master 宕机时会自动将 slave 提升为 master,继续提供服务。 2)、Redis Cluster 着眼于扩展性,在单个 redis 内存不足时,使用 Cluster 进行 分片存储。
Redis 集群方案什么情况下会导致整个集群不可用?
有 A,B,C 三个节点的集群,在没有复制模型的情况下,如果节点 B 失败了, 那么整个集群就会以为缺少 5501-11000 这个范围的槽而不可用。
Redis 支持的 Java 客户端都有哪些?官方推荐用哪个?
Redisson、Jedis、lettuce 等等,官方推荐使用 Redisson。
Jedis 与 Redisson 对比有什么优缺点?
Jedis 是 Redis 的 Java 实现的客户端,其 API 提供了比较全面的 Redis 命令 的支持;Redisson 实现了分布式和可扩展的 Java 数据结构,和 Jedis 相比,功能 较为简单,不支持字符串操作,不支持排序、事务、管道、分区等 Redis 特性。 Redisson 的宗旨是促进使用者对 Redis 的关注分离,从而让使用者能够将精力更 集中地放在处理业务逻辑上。
Redis 如何设置密码及验证密码?
设置密码:config set requirepass 123456 授权密码:auth 123456
说说 Redis 哈希槽的概念?
Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽, 集群的每个节点负责一部分 hash 槽。
Redis 集群的主从复制模型是怎样的?
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所 以集群使用了主从复制模型,每个节点都会有 N-1 个复制品.
Redis 集群会有写操作丢失吗?为什么?
Redis 并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可 能会丢失写操作。
Redis 集群之间是如何复制的?
异步复制
Redis 集群最大节点个数是多少?
16384 个
Redis 集群如何选择数据库?
Redis 集群目前无法做数据库选择,默认在 0 数据库。
怎么测试 Redis 的连通性?
使用 ping 命令。
怎么理解 Redis 事务?
1)事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 2)事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
Redis 事务相关的命令有哪几个?
MULTI、EXEC、DISCARD、WATCH
Redis key 的过期时间和永久有效分别怎么设置?
EXPIRE 和 PERSIST 命令。
Redis 如何做内存优化?
尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用 的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比 如你的 web 系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码 设置单独的 key,而是应该把这个用户的所有信息存储到一张散列表里面.
Redis 回收进程如何工作的?
一个客户端运行了新的命令,添加了新的数据。Redi 检查内存使用情况,如 果大于 maxmemory 的限制, 则根据设定好的策略进行回收。一个新的命令被执 行,等等。所以我们不断地穿越内存限制的边界,通过不断达到边界然后不断地 回收回到边界以下。如果一个命令的结果导致大量内存被使用(例如很大的集合 的交集保存到一个新的键),不用多久内存限制就会被这个内存使用量超越。
都有哪些办法可以降低 Redis 的内存使用情况呢?
如果你使用的是 32 位的 Redis 实例,可以好好利用 Hash,list,sorted set,set 等集合类型数据,因为通常情况下很多小的 Key-Value 可以用更紧凑的方式存放 到一起。
Redis 的内存用完了会发生什么?
如果达到设置的上限,Redis 的写命令会返回错误信息(但是读命令还可以正 常返回。)或者你可以将 Redis 当缓存来使用配置淘汰机制,当 Redis 达到内存 上限时会冲刷掉旧的内容。
一个 Redis 实例最多能存放多少的 keys?List、Set、 Sorted Set 他们最多能存放多少元素?
理论上 Redis 可以处理多达 232 的 keys,并且在实际中进行了测试,每个实 例至少存放了 2 亿 5 千万的 keys。我们正在测试一些较大的值。任何 list、set、 和 sorted set 都可以放 232 个元素。换句话说,Redis 的存储极限是系统中的可 用内存值。
MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如 何保证 redis 中的数据都是热点数据?
Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。 volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最 少使用的数据淘汰 volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过 期的数据淘汰 volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意 选择数据淘汰 allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 no-enviction(驱逐):禁止驱逐数据
Redis 最适合的场景?
1、会话缓存(Session Cache)
最常用的一种使用 Redis 的情景是会话缓存(session cache)。用 Redis 缓存会 话比其他存储(如 Memcached)的优势在于:Redis 提供持久化。当维护一个不 是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不 高兴的,现在,他们还会这样吗? 幸运的是,随着 Redis 这些年的改进,很容 易找到怎么恰当的使用 Redis 来缓存会话的文档。甚至广为人知的商业平台 Magento 也提供 Redis 的插件。
2、全页缓存(FPC)
除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。回到一致性问题, 即使重启了 Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的 下降,这是一个极大改进,类似 PHP 本地 FPC。 再次以 Magento 为例,Magento 提供一个插件来使用 Redis 作为全页缓存后端。 此外,对 WordPress 的用户来 说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加 载你曾浏览过的页面。
3、队列
Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis 能作为一个很好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本 地程序语言(如 Python)对 list 的 push/pop 操作。 如果你快速的在 Google 中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就 是利用 Redis 创建非常好的后端工具,以满足各种队列需求。例如,Celery 有一 个后台就是使用 Redis 作为 broker,你可以从这里去查看。
4,排行榜/计数器
Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序 集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis 只是 正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的 10 个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可: 当然, 这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数, 你需要这样执行: ZRANGE user_scores 0 10 WITHSCORES Agora Games 就 是一个很好的例子,用 Ruby 实现的,它的排行榜就是使用 Redis 来存储数据的, 你可以在这里看到。
5、发布/订阅
最后(但肯定不是最不重要的)是 Redis 的发布/订阅功能。发布/订阅的使用场景 确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚 本触发器,甚至用 Redis 的发布/订阅功能来建立聊天系统!
Redis最适合的场景
Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢? 如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点: 1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。 2 、Redis支持数据的备份,即master-slave模式的数据备份。 3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
(1)、会话缓存(Session Cache)
最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息进行缓存.
(2)、全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。 再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。 此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
(3)、队列
Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。
(4),排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行: ZRANGE user_scores 0 10 WITHSCORES
(5)、发布/订阅
Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已