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过期时间设置的过于集中,到过期的那个时间点全部请求直接走数据库,导致数据库瘫痪,我们一般需要在过期时间上加一个随机值,使得过期时间分散一些。

缓存穿透

查询不存在的值,缓存无法命中,这样每次都会去数据库查。如果大量这种请求过来会导致数据库压力过大。

解决办法:如果数据库也查不到结果就返回一个空值存入缓存。

缓存击穿

某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。

解决办法:

①若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期
②若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
③若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。

Redis异步队列

使用list结构作为队列:
rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。

如果不用sleep,list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。

左边模拟消费者右边模拟生产者:

blpop 30秒,右边在生产数据,左边得到后就消费,并且显示了等待的时间12秒。因此blpop能替代sleep做更精准的阻塞控制。

生产一次消费多次:
使用pub/sub主题订阅者模式,可以实现 1:N 的消息队列。订阅频道--subscribe *(无需创建频道),然后生产者publish * “” 即可
缺点:在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列如RocketMQ等。

Redis持久化

持久化主要是做灾难恢复、数据恢复,也可以归类到高可用的一个环节中去,比如你 Redis 整个挂了。重启 Redis如果没做数据备份,数据都没了。然后就会去数据库查,造成缓存穿透甚至缓存雪崩等。

redis持久化主要是两种方式。
RDB的镜像全量持久化:周期性的快照备份;
AOF的增量持久化:对每条写入命令作为日志,以 append-only 的模式写入一个日志文件中。

通过 RDB 或 AOF,都可以将 Redis 内存中的数据给持久化到磁盘上面来,然后可以将这些数据备份到别的地方去,比如说阿里云等云服务。如果 Redis 挂了,服务器上的内存和磁盘上的数据都丢了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动 Redis,Redis 就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。
如果同时使用 RDB 和 AOF 两种持久化机制,那么在 Redis 重启的时候,会使用 AOF 来重新构建数据,因为 AOF 中的数据更加完整。


这里很好理解,把RDB理解为一整个表全量的数据,AOF理解为每次操作的日志就好了,服务器重启的时候先把表的数据全部搞进去,但是他可能不完整,你再回放一下日志,数据不就完整了嘛。不过Redis本身的机制是 AOF持久化开启且存在AOF文件时,优先加载AOF文件;AOF关闭或者AOF文件不存在时,加载RDB文件;加载AOF/RDB文件城后,Redis启动成功; AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。

RDB 优缺点 

①RDB 会生成多个数据文件,每个数据文件都代表了某一个时刻中 Redis 的数据,这种多个数据文件的方式,非常适合做冷备,可以将这种完整的数据文件发送到一些远程的安全存储上去比如阿里云。
②RDB 对 Redis 对外提供的读写服务,影响非常小,可以让 Redis 保持高性能,因为 Redis 主进程只需要 fork 一个子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化即可。相对于 AOF 持久化机制来说,直接基于 RDB 数据文件来重启和恢复 Redis 进程,更加快速。
③RDB数据快照文件,都是周期性生成一次,这个时候就得接受一旦 Redis 进程宕机,那么会丢失最近 一次快照以后的数据。
④RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒。

AOF 优缺点 

①AOF 可以更好的保护数据不丢失,一般 AOF 会每隔 1 秒,通过一个后台线程执行一次fsync 操作,最多丢失 1 秒钟的数据。
②AOF 日志文件以 append-only 模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复。

RDB 和 AOF 到底该如何选择

Redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。

RDB配置: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<String, String> data = new HashMap<String, String>();
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进行分片存储。

一致性哈希

具体查看这篇文章

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值