首先为什么我们要用Redis?
常用数据类型:
从海量key查询某一前缀的key:
Redis分布式锁
缓存雪崩
Redis异步队列
Redis持久化
Pipeline
Redis的同步机制了解么?
一致性哈希
首先为什么我们要用Redis?
面试官您好。因为传统的关系型数据库如Mysql已经不能适用所有的场景了,比如秒杀的库存扣减,APP首页的访问流量高峰等等,都很容易把数据库打崩,所以引入了缓存中间件,目前市面上比较常用的缓存中间件有 Redis 和 Memcached 不过中和考虑了他们的优缺点,最后选择了Redis。
常用数据类型:
String:一般做一些复杂的计数功能的缓存;
set name "Aa" get name
Hash:单点登录
hmset zhangsan name "zhangsan" age 26 hget zhangsan name
List:做简单的消息队列的功能;
lpush mylist aaa lpush mylist bbb lrange mylist 0 10
set:做全局去重的功能
【元素唯一(add两个相同的,会返回0),无序(add时的顺序与遍历/取出来的顺序不一定一致)】
sorted set:做排行榜应用,取TOP n 操作;延时任务;范围查找;
zadd myzset 3 zhangsan zadd myzset 1 lisi zrangebyscore myzset 0 10
sortedset实现延迟队列:
使用sortedset,拿时间戳作为score,消息内容作为key调用zadd来生产消息,消费者用zrangebyscore指令获取N秒之前的数据轮询进行处理。
从海量key查询某一前缀的key:
1、keys命令,可以一次性查出所有符合的数据,但结果集太大会造成服务卡顿
2、SCAN方式:分批次,基于游标的迭代器,每次随机取出一定数量的数据,可以看出cursor值并不一定是递增的,所以多次取出的数据科能会有重复的。可以用过hashset达到去重的效果。
由于是分批次执行,SCAN总体花费时间比keys长。
Redis分布式锁
分布式锁是控制分布式系统之间共享资源的一种锁,如果共享了某个资源,往往需要互斥来保证一致性。
1、setnx(set if not exist)
SETNX初期被用来解决分布式锁,执行某段代码时可以先用SETNX对某个key设置值,如果设置成功,就证明没有其他线程在执行这段代码。
2、expire
解决setnx长期有效的问题
所以我们似乎可以用setnx+expire组合做一个分布式锁,但其实存在一些问题
这种方式的风险:假设setnx这句程序执行完以后,程序挂掉了。还没来得及执行expire,那么锁就会一直被占用无法释放。
3、set组合命令
伪代码:
缓存雪崩
如果大量的key过期时间设置的过于集中,到过期的那个时间点,Redis可能会出现短暂的卡顿现象。严重的话会出现缓存雪崩,我们一般需要在过期时间上加一个随机值,使得过期时间分散一些。
电商首页经常会使用定时任务刷新缓存,可能大量的数据失效时间都十分集中,如果失效时间一样,又刚好在失效的时间点大量用户涌入,就有可能造成缓存雪崩。
Redis异步队列
使用list结构作为队列:
rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
如果不用sleep,list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
左边模拟消费者右边模拟生产者:
blpop 30秒,右边在生产数据,左边得到后就消费,并且显示了等待的时间12秒。因此blpop能替代sleep做更精准的阻塞控制。
生产一次消费多次:
使用pub/sub主题订阅者模式,可以实现 1:N 的消息队列。订阅频道--subscribe *(无需创建频道),然后生产者publish * “” 即可
缺点:在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如RocketMQ等。
Redis持久化
RDB做镜像全量持久化,AOF做增量持久化。因为RDB会耗费较长时间,不够实时,在停机的时候会导致丢失当前到最近一次快照之间的数据,所以需要AOF来配合使用。在redis实例重启时,会使用RDB持久化文件重新构建内存,再使用AOF重放近期的操作指令来实现完整恢复重启之前的状态。
这里很好理解,把RDB理解为一整个表全量的数据,AOF理解为每次操作的日志就好了,服务器重启的时候先把表的数据全部搞进去,但是他可能不完整,你再回放一下日志,数据不就完整了嘛。不过Redis本身的机制是 AOF持久化开启且存在AOF文件时,优先加载AOF文件;AOF关闭或者AOF文件不存在时,加载RDB文件;加载AOF/RDB文件城后,Redis启动成功; AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。
redis.conf:
规则:
900秒内有1次写入,就触发一次快照(备份)
或300秒内有10次写入,就触发一次快照(备份)
或60秒内有10000写入,就触发一次快照(备份)
当备份进程出错时,主进程就停止写入操作
save:阻塞服务器进程,直到RDB文件被创建完毕;
bgsave:fork出一个子进程来创建RDB文件,不阻塞服务器进程。
自动触发RDB持久化的情形:
RDB原理,其实也就是bgsave原理:
fork出一个子进程来创建RDB文件,子进程创建后,父子进程共享数据段,实现了copy on write。
AOF:记录增删改等变更数据库的指令,以append方式追加到AOF文件
Pipeline
好处:可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性(没有规定先后顺序)。
使用示例:
Jedis redis = new Jedis("192.168.1.111", 6379);
redis.auth("12345678");//授权密码 对应redis.conf的requirepass密码
Map data = new HashMap();
redis.select(8);//使用第8个库
redis.flushDB();//清空第8个库所有数据
Pipeline pipe = redis.pipelined();
start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
data.clear();
data.put("k_" + i, "v_" + i);
pipe.hmset("key_" + i, data); //将值封装到PIPE对象,此时并未执行,还停留在客户端
}
pipe.sync(); //将封装后的PIPE一次性发给redis
Redis的同步机制了解么?
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
- 从服务器连接主服务器,发送SYNC命令;
- 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
- 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
- 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
- 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
- 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;Redis可以使用主从同步,从从同步。
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。增量数据通过AOF日志同步即可,有点类似数据库的binlog。
主从模式弊端很明显,master宕机后,就无法提供写操作了。
是否使用过Redis集群,集群的高可用怎么保证,集群的原理是什么?
Redis Sentinal 着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster 着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
一致性哈希
具体查看这篇文章



