Redis 常见问题
1.Redis支持的数据类型?
-
String 字符串
-
List 列表 相当于Java里的LinkedList 常用于消息队列和异步逻辑处理
- lpush 插入值到列表头部
- rpush 插入值到列表尾部(这个用于实现队列右进左出)
rpush books java python go
- blpop 取出列表第一个元素,当没有元素时会等待N秒,超时返回nil [
blpop xxx 10
] - brpop 取出列表最后一个元素
- LINSERT 在某个list列表前或后插入元素,当这个列表为空时则不做任何操作 [
LINSERT list1 BEFORE "bar" "Yes"
] - lindex 根据下标获取列表值 负数代表倒数几位 [
lindex course 1
] - llen 返回列表长度 [
llen course
] - lrange 返回下标范围内的元素列 [
lrange course 0 2
]
-
Set 集合 相当于Java的HashSet 键值无序唯一 常用与去重功能
- sadd 插入值 [
sadd user user1
] - smembers 返回该key所有的集合 [
smembers user
] - sismember 判断这个值是否存在 [
sismember user user1
] - scard 返回列表长度 [
scard user
]
- sadd 插入值 [
-
ZSet 有序列表 有序的HashMap 并且每个值都赋予一个score代表权重排序 常用与关注时间列表排序、名次排名等
- zadd 插入值 [
zadd books 8.9 "21天学会Java"
] - zrange 按score排序列出,可筛选出范围 [
zrange books 0 -1
] - zrevrange 按score逆序列出 [
zrevrange books 0 -1
] - zcard 返回列表长度 [
zcard books
] - zscore 获取指定value的score(因为score是用double存储,因此会存在小数点精度问题) [
zscore books "21天学会Java"
] - zrank 获取排名 [
zrank books "21天学会Java"
] - zrangebyscore 返回根据score筛选的列表(这里的值无穷大可以用inf代替) [
zrangebyscore books 0 8.91
] | [zrangebyscore books -inf 8.91 withscores
] - zrem 删除该value [
zrem books "21天学会Java"
]
- zadd 插入值 [
-
Hash 字段 相当于Java里的HashMap 实现为数组+链表 常用于存储属性
- hset 插入一条数据 [
hset user1 age 29
] - hincrby 计数 [
hincrby user1 age 1
]
- hset 插入一条数据 [
2.什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
Redis持久化是为了避免进程崩溃导致数据丢失问题
主要有RDB和AOF两种持久化方式
RDB
RDB会每隔一段时间把内存数据以二进制文件形式写入磁盘。文件名为dump.rdb
RDB有三种机制:save、bgsave、自动化
save命令会阻塞redis服务器,直到RDB完成为止。
bgsave命令会创建一个子进程异步进行RDB,创建过程会阻塞。
自动化是在redis.conf配置好规则进行自动触发。
优点是全量备份,这样在恢复数据时速度会比AOP快。
缺点是在全量备份时,父进程在被修改数据时,子进程在做备份时有可能会忽略了数据,因此在持久化期间修改的数据无法保存下来,可能会丢失数据
AOF
AOF会把所有接收到的命令以命令文本方式一行一行的追加到备份文件中,其实就是日志记录。
AOF有三种机制:always、everysec、no
always只要有数据变更就会立刻记录到磁盘中,因此性能较差但数据完整性很好
everysec是异步操作,并不会马上记录到磁盘而是每秒一个异步进程来做记录磁盘操作,因此如果在异步步骤宕机,那就会丢失数据。
on是从不同不
优点是能够准时的录入磁盘防止丢失数据
缺点是恢复数据时比RDB慢,因为AOF是保存命令机制,因此恢复时会讲所有的命令从头开始执行一遍,还有就是在性能消耗上,AOF每秒都要执行一次记录,因此会消耗少许的性能。
3.Redis 有哪些架构模式?讲讲各自的特点
Redis主要有单机模式(主从复制)、哨兵、集群(proxy、直连)
单机模式:容量有限、不能保证高可用
主从复制:主要从master复制数据到从库中,从而降低master的读取压力
哨兵:结构依然是主从模式,新增sentinel的分布式监控系统,当监控到master有异常时,会进行自动迁移选举master。这样保证了高可用,缺点是依然是主从模式,没有办法解决master写入的压力。
集群直连:redis3.0后新增redis-cluster的集群概念,主要是采用无中心结构,每个节点互相同步
4.使用过Redis分布式锁么,它是怎么实现的?
首先Redis的操作是可以保证原子性的,因为Redis是单线程的关系。
其次,光一个操作具有原子性是不够的,当我们使用setnx执行添加缓存,再添加expire,这样拆分开两个指令的话,假设在setnx成功后服务器挂了,这样没有过期时间就会造成死锁。因此使用整合成一套指令的 jedis.set(String key, String value, String nxxx, String expx, int time)
这个实现方法是最佳选择,当然传LUA脚本来实现也是可以的。
5.使用过Redis做异步队列么,你是怎么用的?有什么缺点?
- 生产者与消费者模式:使用上文提到的list的rpush生产消息,lpop消费消息,缺点是
- 发布订阅者模式:使用pub/sub订阅者模式,实现1:N消息队列。
6.什么是缓存穿透?如何避免?什么是缓存雪崩?如何避免?
缓存穿透就是当用户去查询数据时,发现缓存没有命中,而接着去数据库查也没找到数据,这样会给数据库带来很大的压力。
这种情况有可能是受到攻击,把一些不存在的值大量传过来,因此解决方案为
- 加强基础值校验
- 缓存空对象,设置一个较短的时间,以保证不被反复攻击
缓存击穿是指缓存没有但数据库中有数据,导致数据库压力过大
- 热点数据先缓存或永不过期
缓存雪崩就是大批数据同时过期,使得查询数据量过大,引起数据库压力过大导致崩溃
与缓存击穿不同在于 缓存击穿是针对同一个数据,而缓存雪崩是不同数据同一时间过期
- 数据库过期时间随机过期
- 限流降级
7.为什么Redis 单线程却能支撑高并发?
- redis是基于内存的,内存的速写速度非常快
- redis是基于非阻塞IO多路复用机制
- redis单线程,省去切换线程的时间
8.说说Redis的内存淘汰策略
- 定时扫描策略
- Redis默认每秒进行10次过期扫描,每次从过期字典中随机选出20个key
- 删除这20个key中已过期的key
- 如果过期的key的比例超过1/4,则继续重复第一步。
- 惰性次略
- 当客户端访问key时,Redis会对key进行过期时间检查,如果过期就立即删除。
9.说说从节点的过期策略
从节点是不会进行扫描的,主节点的key到期时会从AOF文件添加一条del的指令,然后同步到从节点,从节点通过执行这条指令来实现删除过期的key。
10.Redis的并发竞争问题如何解决?
- redis分布式锁
- 客户端加锁(synchronized)
11.Redis中List和ZSet的存储结构是怎么实现的
List
List内部每个元素是使用ziplist的存储结构来实现的,即压缩列表,当数据少时他会分配一块连续的内存给他用于存储,当数据多了之后会改为quicklist的存储方式
ziplist是一种特殊的双向链表,他内部并没有双向指针,而是存储上一个元素的长度和当前元素的长度,通过长度推算下一个元素。
quickList其实就是把linkedlist和ziplist结合到一起,当使用quickList时,就不会继续使用ziplist的逻辑,quickList会为每个ziplist创建一个quicklistNode,然后在quicklistNode里包含ziplist,quicklistNode来实现prev和next指针。
ZSet
是使用数据结构跳跃列表
实现的,这块暂时还不熟悉,后续会单独写一篇来详细介绍