为什么要使用redis
redis的产生是因为通常的数据存储介质如数据库面临高并发场景的时候响应速度和吞吐量都出现了瓶颈,满足不了高并发的需求。如果只使用jvm级别缓存又会占用宝贵的服务器cpu和内存资源,同样会产生瓶颈,影响我们业务处理能力。jvm级别缓存也满足不了高可用的需求,系统挂掉缓存也就跟着没了。
redis使用场景不止局限于帮助数据库突破瓶颈。
1)传统的多读少写场景可以使用redis
2)某些即时场景,会频繁产生一些临时数据不需要存储,也可以用redis
3)某些频繁更新的场景,且并发量较大的场景,即使需要数据库需要存储也会遇到瓶颈问题,可以考虑用redis缓冲,定时同步。
安装
-
直接取官网下载tar包,本地解压。根目录下我们会看到redis.conf、sentinel.conf这些配置文件模板。启动的时候我们可以把他们根据环境调整,作为启动参数。
-
进入目录直接执行make,成功后在src目录下会出现redis-cli、redis-server、redis-sentinel这些可执行文件。
redis.conf
启动redis最好使用配置文件的方式,方便自定义
./redis-server ../redis.conf
以下是一些常用参数(持续整理):
//默认本机ip,我们可以改成0.0.0.0,这样其他服务可以通过本机的网卡直接访问到redis服务。
bind 127.0.0.1
// 端口,根据需求自行调整
port 6379
// 改成yes就变成后台进程
daemonize no
常用命令(持续整理):
// 首先连接服务器
redis-cli -p [port] -h host
// 查看服务状态,包含所有服务相关信息和使用状态
info
连通性测试
redis-cli -h host -p port -a password
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
或者使用redis的客户端java api
Jedis jedis=new Jedis("127.0.0.1",6379);
//查看服务器是否运行,打出 pong 表示OK
System.out.println("ping redis:" + jedis.ping());
数据类型
redis支持5种数据类型:string、hash、list、set、zset(有序set)。
redis的key只能是String类型,最大512M。
redis的value 可以多种类型,如String、Hash、List、Set、SortSet等。
redis的持久化
-
RDB:根据规则(定期内触发几次命令就保存)备份最终数据结果,不同于AOF备份的是操作日志。RDB的最大缺电是容易出现数据丢失,因为规则是基于时间段规则,规定时间段内服务down调就没办法。
-
AOF: 命令的操作日志保存,可以定时间隔备份,也可以每次执行命令都备份。安全性比RDB更好,当然性能影响也较大。
redis的内存管理
hash、list、set存储的上限都为2的32次方减1个,不到43亿个数据。
内存压缩:
redis的内存压缩虽然会减少空间占用,但是会消耗cpu资源,这会影响redis的性能,所以redis的压缩规则是超过一定数值(比如512个)就不压缩。如果大小超出了压缩范围,redis也将把数据变回压缩前大小。
可见redis的设计是性能优先的,这也是使用redis的目的。
过期处理策略:
1)主动处理策略:redis不会统计所有key,而是记录被设置了过期时间的key,每1秒执行10次,从中抽取20个key检查,过期就删掉,如果超过25%的key过期,则从新开始抽取。
2)被动处理策略:如果命令访问的key已经过期,则删除。
数据持久化过程中的过期数据处理:
1)RDB:RDB备份过程中如果数据还没有过期则不会删除,恢复后照常按照主动、被动处理策略执行。
2)AOF:AOF备份恢复过程中如果发现某些数据已经过期,则会自动追加一条删除命令,保证在数据加载的过程中就被删掉。
注意过期策略是依据系统服务时间的,系统时间错的话会影响到缓存过期执行结果。
内存回收策略:内存满了的处理策略
1)noevication:不回收,满了再插入就报错。
2)lru系列:根据命中次数多少来回收内存。lru算法并不是处理所有的key,而是会先按百分比采样,然后再使用回收策略。
allkeys-lru(所有key中执行lru算法)
volatile-lru(过期的key中执行lru算法)
3)lfu系列:根据最近使用频次程度回收内存。具体实现是使用一个基于概率的对数计数器,并且计数会随着时间的推移减少,以此来达到近似的使用频次判定。lfu算法同样要先经过采样再执行。
allkeys-lfu(所有key中执行lfu算法)
volatile-lfu(过期的key中执行lfu算法)
4)random系列:随机回收。
allkeys-random(所有key中执行random算法)
volatile-random(过期的key中执行random算法)
5)volatile-ttl:回收已经过期的key,优先回收存活时间较短的key。
redis的主从复制
主要解决了2个问题:
1)单点故障
2)单机QPS瓶颈:单机顶多也就10-12万的样子,上百万的并发必然应对不了。
具体使用方式:
1)命令行:
slaveof [ip] [port]
slaveof no one
- 配置文件:
slaveof [ip] [port]
slave-read-only yes
主从复制的流程:
1)从服务向主服务发送当前同步进度和同步源服务的id
2)主服务如果还是源服务,则增量同步。
3)主服务如果不是源服务(原来的master宕机等原因),则全量同步。主服务器会把全量数据生成一份rdb传输给从服务,再加载到从服务的内存。
主从复制是异步的,如果是全量复制,主服务器可以正常接收命令,但是从服务是会阻塞。
场景:
1)读写分离:主支持读写,从支持读。既可以解决单点压力大的问题,还可以保证写的安全。
2)另一个技巧:主不支持持久化,让从服务做持久化,可以提高主服务的读性能。
故障迁移问题:虽然主服务不持久化可以提高读性能,但是如果主服务器如果down掉,就没法恢复数据,如果设置自动重启会导致从服务器的数据也同步为空,进而完全丢失数据。解决办法是先让从服务器slaveof no one退出集群,再变成主服务器,让故障恢复的机器变成从服务器。或者是再主服务器重启之前,将从服务器的备份rdb文件copy回主服务器。
redis哨兵
服务发现和健康检查:redis的哨兵可以帮我们监控redis集群的状态,并且告知业务服务器当前master节点的地址。健康检查的原理其实就是先通过info命令获取redis主从服务器的信息,然后通过ping命令去探知redis服务器是否存活,为了保证系统高可用,哨兵也需要至少部署3台,如果一段时间内redis的master节点没响应,并且至少2台哨兵发现了这个问题,则会认为当前master不可用,需要执行故障迁移。同时改动态改写哨兵的配置文件,追加当前故障迁移的信息。
故障迁移:如果redis的master挂掉,哨兵可以自动帮我们切换master节点。
哨兵的主观下线,客观下线:主观下线是指单个哨兵认为master节点已经下线了,客观下线是多个哨兵通过命令沟通的结果认定master节点已经下线(根据配置,只要有2台哨兵主观下线就算客观下线)。哨兵互相发现是利用的redis的发布订阅功能,借助通道互相发现的。发现后哨兵的通讯是直接发命令通讯。哨兵之间的通讯也会相互订阅。
哨兵选举机制:选举机制是用来选择一个主节点执行故障迁移,是通基于Raft算法实现的选举规则,主节点发现故障后,每个从节点都会先希望自己成为主节点,同时通过命令向其他节点拉票,每个节点只有一个投票机会,最终如果有节点票数过半就会自动成为主节点并且去执行故障转移。如果一段时间内没有那个节点实际执行了故障迁移,则会从新执行选举。每个节点拉票的时候会有一个随机数控制的睡眠时间,这样就会出现时间差别,就会更容易获得过半票数。
redis服务master节点选取规则:按下面的顺序依次判断。
1)查找健康的从节点。如果只有一个是健康的则直接选定为master节点。
2)查找从节点是否配置了权限(conf中),如果有选择最大的一个。
3)多个查看多个节点的备份数据,同步进度最大的成为master节点。
4)最后的办法,根据run id的排序选择。
redis分片存储
主从读写分离可以帮我们解决写的压力,
哨兵高可用可以帮我们解决高可用的问题,
但是还有别的问题:
1)写的压力依旧解决不了
2)单机内存不够用了
这时候就出现了redis的分片存储策略。
同时需要事先说明,master分片后,主从读写分离默认是不可用的,集群会自动把从节点上的读写重定向主节点上,从节点已经完全成为一个备份节点,只保证高可用。
redis会把多个分片节点看作列车车厢,每一小段内存当做座位(slot),这样多个节点的内存就连续成一个大的内存,不管多少节点多少内存,都会平分为16384个slot,然后每次存值的时候就可以通过hash算法得出存储key的位置,这样就实现了分片存储。这个过程还有一个小细节就是,首次客户端有可能因为某些原因发错了分片节点,节点会告知服务端发送错了,然后会重定向到正确的分片节点,客户端自己除了可以计算hash值,还会缓存key的位置,这样就不至于每次都计算再重定向,影响性能。如果有新的分片节点加入,客户端插入失败后会重新计算分配节点,重新计算hash。
使用redis的出发点是解决性能问题,所以设计上并不能保证主从数据一致,这也是redis不能替代传统关系数据库的原因。
缓存相关的安全问题
redis可以提高并发处理性能,但是并不能完全保护数据库服务,万一出现缓存击穿或者缓存失效,脆弱的数据库就很容易崩溃,从而导致系统雪崩,所以我们在实际使用场景我们还需要配合开发一定的缓存限流功能(比如信号量,漏桶)来保护数据库,万一缓存失效,顶多也就是个降级处理。
还有一种情况,就是有人故意使用不存在的id去高频访问系统,就相当于直接绕开缓存区去攻击数据库,面对这种情况,我们需要一种策略来记录当前key对应的数据数据库是否存在,当有人使用不存在的id攻击系统的时候,在缓存一层就可以发现并拦截处理掉,从而保护脆弱的数据库系统。我们通常会使用布隆过滤器(Bloom Filter)来实现这种方案。
使用布隆过滤器并不能保证hash冲突,但是绝大多情况下不会冲突,使用布隆过滤器的目的是为了缓解服务的压力,并不能做到杜绝。
网友评论