既然是基础了,就不会有太多的源码。
基础中的基础知识:
缓存穿透:某个数据,redis没有,下次访问直接去数据库,如果请求量很大的话就会导致数据库压力增大
解决方案:1.缓存空对象
2.加锁只允许一个线程进去,其余的线程慢慢等。 缺点是慢啊
缓存击穿:就是某个时间点某个缓存突然失效,然后大量访问直接去访问到了数据库。
解决方案:1.热点数据永远不过期。 缺点是用多了redis可能会满
2.加锁
缓存雪崩:一堆缓存都到期了,然后大量访问。覆巢之下,焉有完卵?
解决办法:1.加机器,加redis
2.服务降级。加锁/队列来控制查询的数量
3.提前吧热点数据加到redis里面去,提前访问。
几种数据库类型存储结构的区别
1.key-value 键值对,优点是查的快,缺点是拓展性不高,
2.列存储,其实就是按照key来分组,相同的key的value放在一个list里面Map<string,list<> >,优点是拓展性强,因为value很多节点 缺点是因为value是个很大的数据库集合,所以查询性能比直接key-value的不高
3.文档型数据库:相同的key的value分组,value是个有结构的存储,优点是这个有结构的存储是可以变得,缺点是查询慢,
4.图形数据库:就是表与表直接关系化,联查。节点和关系组成的图。优点是计算快,缺点是更新可能涉及表变更的有点多,所以会比较慢
内存断电就丢了
-----------------------------------------------------------------------------start--------------------------------------------------------------------------------------------------------------------
redis持久化机制有:RDB(Redis DataBase)和AOF(Append Only File)。
数据在持久化的过程:1.在客户端内存中 2.在服务器内存中 3.在服务器系统内存缓冲区中。4.在磁盘缓存中 5.真正在磁盘中
RDB其实就是把数据以快照的形式保存在磁盘上,在指定的时间间隔内将内存中的数据集快照批量写入磁盘。提供了三种机制:save、bgsave、自动化。
save触发方式
该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。
2、bgsave触发方式:异步非阻塞的save
3、自动触发
自动触发是由我们的配置文件来完成的。在redis.conf配置文件中,
参考自https://baijiahao.baidu.com/s?id=1654694618189745916&wfr=spider&for=pc
配置包含
①save:这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave。
②stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
③rdbcompression ;默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
④rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
⑤dbfilename :设置快照的文件名,默认是 dump.rdb
⑥dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。
rbd优点是全量备份,直接对所有的进行快照,所以适合全量备份容灾性好,而且因为是异步,所以不会影响到主线程
缺点:在异步的时候,如果数据有变更是不知道的,所以可能会导致丢数据
AOF就是追加新数据到文件中。每当有一个写命令过来时,就直接保存在我们的AOF文件中。
AOF也有三个触发机制
aof优点:异步的话丢失少,因为每秒一次轮询,最多丢一秒的数据;写入性能高,即使文件大,也打不过整个文件重写,每次修改都是一部分。
缺点:文件大,因为aof是写rof文件,所以比一个数据快照的要大;每秒一次记录同步数据,所以会造成性能损失(虽然不大),但是写的qps还是比rdb的低
进入redis
首先启动redis 进入redis的src目录: redis-server …/redis.conf
然后 redis-cli -h 127.0.0.1 -p 6379
知识点
redis’有16个数据库,默认是第0个。可以通过select来切换数据库
127.0.0.1:6379> select 1 // 切换到第八个数据库
OK
127.0.0.1:6379[1]>
127.0.0.1:6379> DBSIZE // 查看数据库大小
(integer) 1
127.0.0.1:6379[1]> FLUSHALL // 清空所有数据库
OK
127.0.0.1:6379[1]>
127.0.0.1:6379[1]>
127.0.0.1:6379[1]>
127.0.0.1:6379[1]> FLUSHDB // 清空单发给钱数据库
OK
127.0.0.1:6379[1]>
127.0.0.1:6379> FLUSHALL
(error) MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option). Please check the Redis logs for details about the RDB error.
// 如果遇到这个错误 这是由于强制停止redis快照,不能持久化引起的 解决方案如下
127.0.0.1:6379> config set stop-writes-on-bgsave-error no
OK
127.0.0.1:6379> FLUSHALL
OK
因为redis是基于内存操作,所以redis的瓶颈是基于带宽和机器内存的。
redis是单线程但是速度快的原因:因为基于内存操作,但是内存又没有上下文切换的操作,所以单线程更快。多次读写都是在一个cpu上。
redis五大基本类型:string list set(集合是无序不可重复的)、 zset(SortedSet 集合是有序不可重复的、命令为zadd 和zrange) hash(相当于是一个key中存在多个map,命令为hget和hset)。三大特殊类型:geospatial 地理位置(底层实现zset(zset的命令它也支持) 命令为geoadd 和一系列自带计算的工具)、hyperloglog
String :包含java中很多的string类型的操作
比如截取字符串,追加字符串(APPEND wddKey wddValue),数值增加(INVRBY wddKey 28),数值减少(DECRBY wddKey 12)
127.0.0.1:6379> GETRANGE wddKey 0 3 // 截取下标0-3的 类似string的subString
helL"'
127 .0.0.1 :6379>
127.0.0.1:6379> SETRANGE wddKey 2 DDD // 将下标为2的替换成DDD,类似string的replace
heDDL"'
127.0.0.1:6379> SETEX wddKey 30 ddd // 如果存在才设置 set if exit, 30S过期
127.0.0.1:6379> SETNX wddKey 30 ddd // 如果不存在才设置 set not exit, 30S过期
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 //同时设置多 个值
OK
127.0.0.1:6379> mget k1 k2 k3 // 同时 获取多个值
127. 0.0.1:6379> msetnx k1 v1 k4 v4 //msetnx是一个原子性的操作, 一起成功,一起失败
I
(integer) 0
127.0.0.1:6379> get k4
(ni1)
关于list
127.0.0.1:6379> LPUSH wddList wddf wdds wddt // 新增数据
(integer) 3
127.0.0.1:6379> LPUSH wddList wddf wdds wddt
(integer) 6
127.0.0.1:6379> get wddList // list获取不能用get
(error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> LRANGE wddList 0 -1 // 获取list的所有数据
1) "wddt"
2) "wdds"
3) "wddf"
4) "wddt"
5) "wdds"
6) "wddf"127.0.0.1:6379> LSET wddList 1 wddd // 讲list下标为1的数据替换
OK
127.0.0.1:6379> LRANGE wddList 0 -1
1) "wddt"
2) "wddd"
3) "wddf"
4) "wddt"
5) "wdds"
6) "wddf"
127.0.0.1:6379> LPOP wddList // 前面是l就是去掉列表的最后一个数据 前面是R就是去掉列表的第一个数据
"wddt"
127.0.0.1:6379> LPUSH wddList wddfour // 前面是l是将数据放到list的第一位 前面是R是将数据放到list的最后一位
(integer) 6
127.0.0.1:6379> LRANGE wddList 0 -1
1) "wddfour"
2) "wddd"
3) "wddf"
4) "wddt"
5) "wdds"
6) "wddf"
127.0.0.1:6379> LINDEX wddList 1 // 获取下标为1的数据
"wddd"
127.0.0.1:6379> LLEN wddList // 约等于 list.size()
(integer) 6
127.0.0.1:6379> LREM wddList 2 wddf // 移除掉列表前面两个为wddf的数据
(integer) 2
127.0.0.1:6379> RPOPLPUSH wddList anotherList // 将wddList的最后一位数据移到anotherList的第一位,看清了 只有RpopLpush,没有Lpop rpush之类的排列组合
"wddf"
127.0.0.1:6379> EXISTS ddddd // 判断列表是否存在
(integer) 0
关于set
127.0.0.1:6379> sadd myset "he11o" # set集合中添加元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
#查看指定set的所有值
1) "he1lo"
127.0.0.1:6379> SISMEMBER myset he11o #判断某一个值是不是在set集合中!
(integer) 1
127.0.0.1:6379> SISMEMBER myset world
(integer) 0
127.0.0.1:6379> SRANDMEMBER myset 2 #随机抽选出2个元素
"he1lo"
关于hash
命令略
适用的地方:经常会发生变更的对象存储。
关于Zset(有序集合)
在set的基础上,加了一个排序。
127.0.0.1:6379> ZADD wddZset 0 wdd0 3 wdd3 2 wdd2 5 wdd5
(integer) 4
127.0.0.1:6379>
127.0.0.1:6379>
127.0.0.1:6379> ZRANGE wddZset 0 -1 // 按照sort 排序
1) "wdd0"
2) "wdd2"
3) "wdd3"
4) "wdd5"
127.0.0.1:6379> ZSCORE wddZset wdd3 // 返回有序集合中指定成员名为wdd3的value
"3"
127.0.0.1:6379> ZINCRBY wddZset 11 wdd0 // 将值的sort + 11 可以用负数
"11"
127.0.0.1:6379> ZRANGE wddZset 0 -1
1) "wdd2"
2) "wdd3"
3) "wdd5"
4) "wdd0"
127.0.0.1:6379> ZCOUNT wddZset 3 20 // 获取区间位于 3-20的数量
(integer) 3
127.0.0.1:6379> ZREVRANGE wddZset 2 5 // 从sort 高到低返回下标为2-5的数据
1) "wdd3"
2) "wdd2"
127.0.0.1:6379> ZREM wddZset wdd2 // 移除属性为wdd2的值
(integer) 1
zset主要用于排行榜之类的
// 特殊。。。。。。。。。。。。。。。。。。。。。
关于GEO(基数统计)
地图的一些功能,比如经纬度添加( GEO ADD ),
127.0.0.1:6379> GEOADD wddGeoKey 116.394079 40.09315 sanxingsicun // 添加地区
(integer) 1
127.0.0.1:6379> GEOADD wddGeoKey 114.42211 23.108738 huaMaoDaSha
(integer) 1
127.0.0.1:6379> GEOADD wddGeoKey 114.421766 23.111487 huaMaoTianDi
(integer) 1
127.0.0.1:6379> GEOPOS wddGeoKey sanxingsicun // 获取指定地区经纬度
1) 1) "116.3940766453742981"
2) "40.09315091345487048"
127.0.0.1:6379> GEODIST wddGeoKey huaMaoDaSha huaMaoTianDi
"307.9012"
127.0.0.1:6379> GEODIST wddGeoKey huaMaoDaSha huaMaoTianDi km // 获取两个点之间的距离 没有km则表示单位的m,当然还有其他的单位可以
"0.3079"
127.0.0.1:6379> GEORADIUS wddGeoKey 114.421 23.11 10 km // 获取某个经纬度指定距离内的地区
1) "huaMaoDaSha"
2) "huaMaoTianDi"
127.0.0.1:6379> GEORADIUSBYMEMBER wddGeoKey huaMaoDaSha 100 km // 获取华贸大厦半径100Km的地区
1) "huaMaoDaSha"
2) "huaMaoTianDi"
127.0.0.1:6379> GEOHASH wddGeoKey huaMaoDaSha // 获取华贸大厦的geoHash值
1) "ws177wzdhk0"
关于Hyperloglog(基数统计) -----redis 2.8.9及之后才有这个结构
两个数组中,不重复的数就是基数
基数统计就是去重后数量
比如求网页的UV (一个人访问一个网站多次,但是还是算作一个人)
传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断,如果用户的数量一多,set就很大了。如果这些数据仅仅是用来统计基数,那么无疑是造成了巨大的浪费
那么为什么不用bitMap呢?
bitmap 就是用给一个bit位来标记某个元素对应的 value . 而 key 就是这个元素.
统计1亿个数据的基数,需要的内存是:1E/8/1024/1024 ≈ 12M。
虽然bitmap在节省空间方面已经有了不错的表现,但是如果需要统计1000个对象,就需要大约12G的内存,显然这个结果仍然不能令我们满意。在这种情况下,HyperLogLog将会出来拯救我们。
我们的目的是为了计数,而不是保存用户id ;
HyperLogLog实际上不会存储每个元素的值,它使用的是概率算法,通过存储元素的hash值的第一个1的位置,来计算元素数量。这也就是为什么hyperLogLog没有get方法的原因
容错率0.81%,适合不重要的数据。比如统计在线人数
127.0.0.1:6379> PFADD wddHllKey1 1 3 5 a b d e // 添加数
(integer) 1
127.0.0.1:6379> PFADD wddHllKey2 1 1 3 3 5 7 a b c j
(integer) 1
127.0.0.1:6379> PFCOUNT wddHllKey2 // 统计这个key里面的基数个数 因为去掉了重复的1和3 所以只有8个
(integer) 8
127.0.0.1:6379> PFMERGE wddNewKey wddHllKey1 wddHllKey2 // 将两个key去掉重复的值,合并为一个新的叫wddNewKey中
OK
127.0.0.1:6379> PFCOUNT wddNewKey // 去掉了重复的 1 3 5 a b
(integer) 10
关于bitMap
因为bitMap是通过二进制来存储的,非0即1
比如中国有14E个人口,则需要14E个bite来存。初始都是设置成0,比如n个人买房了,则吧这n个人的值从0改为1
1个字节=8个bite
优势
1.基于最小的单位bit进行存储,所以非常省空间。
2.设置时候时间复杂度O(1)、读取时候时间复杂度O(n),操作是非常快的。
3.二进制数据的存储,进行相关计算的时候非常快。
4.方便扩容
限制
redis中bit映射被限制在512MB之内,所以最大是2^32位。建议每个key的位数都控制下,因为读取时候时间复杂度O(n),越大的串读的时间花销越多。
使用场景
每个key只记录当前业务属性的状态,每个uid当作bit位来记录信息(用户超过2^32内需要分片存储)。
相对于hyperLogLog,可以用bitMap来存储比较重要的数据(因为是直接存数据),hyperLogLog存不重要的(因为是猜数据)。
127.0.0.1:6379> SETBIT wddBitMap 1 1 // 添加值
(integer) 0
127.0.0.1:6379> SETBIT wddBitMap 2 1
(integer) 0
127.0.0.1:6379> SETBIT wddBitMap 5 1
(integer) 0
127.0.0.1:6379> SETBIT wddBitMap 0 0
(integer) 0
127.0.0.1:6379> SETBIT wddBitMap 3 0
(integer) 0
127.0.0.1:6379> SETBIT wddBitMap 4 0
(integer) 0
127.0.0.1:6379> GETBIT wddBitMap 1 // 获取下标的值
(integer) 1
127.0.0.1:6379> BITCOUNT wddBitMap // 获取值为1的数量
(integer) 3
reids事务相关
关于事务:如果出现了编译时异常,则事务整个都会丢掉不执行。
如果是运行时异常,则整个事务都会执行,只不过不执行错误的那部分
127.0.0.1:6379> MULTI // 开启事务
OK
127.0.0.1:6379> set wddKey1 1
QUEUED
127.0.0.1:6379> set wddKey2 2
QUEUED
127.0.0.1:6379> set wddKey3 2
QUEUED
127.0.0.1:6379> set wddKey4 4
QUEUED
127.0.0.1:6379> get wddKey1
QUEUED
127.0.0.1:6379> exec // 事务提交
1) OK
2) OK
3) OK
4) OK
5) "1"
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379>
127.0.0.1:6379> get wddKey1
QUEUED
127.0.0.1:6379> set wddKey4 4
QUEUED
127.0.0.1:6379>
127.0.0.1:6379> get wddKey4
QUEUED
127.0.0.1:6379>
127.0.0.1:6379>
127.0.0.1:6379> DISCARD // 放弃事务提交 全部回滚 取消全部队列
OK
watch
监控某个数据,如果这个数据在执行队列之前被修改了,则整个事务为失败。类似于乐观锁的version。
当客户端断开连接时,该客户端对键的监视也会被取消。
unwatch命令可以手动取消对所有键的监视。
关于redis配置文件的
守护进程和后台进程的区别:
1.守护进程已经完全脱离终端控制台了,而后台程序并未完全脱离终端(在终端未关闭前还是会往终端输出结果)
2.守护进程在关闭终端控制台时不会受影响,而后台程序会随用户退出而停止,需要在以nohup command & 格式运行才能避免影响;
3、守护进程的会话组和当前目录,文件描述符都是独立的。后台运行只是终端进行了一次fork,让程序在后台执行,这些都没改变。
举个例子:比如监听服务,你把vmware软件退出了,服务是不会退出的,这就叫守护进程。而普通的放在后台运行的叫后台进程。你把电脑关机了之后才消息。
网络之类的略
################################# GENERAL #####################################
protected-mode yes // 保护模式,默认是开启状态,只允许本地客户端连接, 可以设置密码或添加bind来连接
daemonize no // 是否开启守护进程 当redis作为守护进程运行的时候,它会写一个 pid 到 /var/run/redis.pid 文件里面
pidfile /var/redis/run/redis_6379.pid // 配置开启守护进程后的PID文件路径
supervised // 就是守护进程以什么样的方式启动。
supervised no - 没有监督互动
supervised upstart - 通过将Redis置于SIGSTOP模式来启动信号
supervised systemd - signal systemd将READY = 1写入$ NOTIFY_SOCKET
supervised auto - 检测upstart或systemd方法基于 UPSTART_JOB或NOTIFY_SOCKET环境变量
loglevel notice // 日志级别 分为 debug verbose notice warning
logfile /var/redis/log/redis_6379.log // 日志文件的位置,当指定为""时,为标准输出,如果redis已守护进程模式运行,那么日志将会输出到/dev/null
databases 16 // 设置数据库的数目
################################ SNAPSHOTTING ################################
save 900 1 // 900 秒内如果至少有 1 个 key 的值变化,则保存
save 300 10 // 300 秒内如果至少有 10 个 key 的值变化,则保存
save 60 10000 // 60 秒内如果至少有 10000 个 key 的值变化,则保存
save "" // 停用保存功能
stop-writes-on-bgsave-error yes // 持久化出错了,还要继续接受所有的写请求
rdbcompression yes // 是否需要压缩rdb文件 压缩会损耗cpu资源,前面在说rdb特性的时候已经说了
rdbchecksum yes // 保存rdb文件的时候,是否要进行错误的校验
dbfilename dump.rdb
dir ./ // rdb文件默认保存目录 默认为当前文件夹
################################## SECURITY ###################################
requirepass 123456 // 设置登录密码,如果设置了后没有输入密码 则不能请求 解决方式为
127.0.0.1:6379> auth 123456
OK
################################### CLIENTS ####################################
maxclients 10000 // 设置连接redis的最大客户端的数量 为0 表示不作限制 当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
############################## MEMORY MANAGEMENT ################################
maxmemory <bytes> // 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。
maxmemory-policy noeviction // 当内存使用达到最大值时,redis使用的清楚策略。有以下几种可以选择:
1)volatile-lru 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used )
2)allkeys-lru 利用LRU算法移除任何key
3)volatile-random 移除设置过过期时间的随机key
4)allkeys-random 移除随机ke
5)volatile-ttl 移除即将过期的key(minor TTL)
6)noeviction noeviction 不移除任何key,只是返回一个写错误 ,默认选项
maxmemory-samples 3 // LRU 和 minimal TTL 算法都不是精准的算法,但是相对精确的算法(为了节省内存) 随意你可以选择样本大小进行检
############################## APPEND ONLY MODE ###############################
就是aof模式
appendonly no // 是否开启aof模式
appendfilename "appendonly.aof" // 文件名
appendfsync always // 表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快 Linux的默认fsync策略是30秒。可能丢失30秒数据。
appendfsync everysec // 表示每次写入都执行fsync,以保证数据同步到磁盘。 慢啊
appendfsync no // 表示每秒执行一次fsync,可能会导致丢失这1s数据 轮询
no-appendfsync-on-rewrite no // 设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。
auto-aof-rewrite-percentage 100 // aof自动重写配置,当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,
auto-aof-rewrite-min-size 64mb // 设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
aof-load-truncated yes // 如果选择的是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。 如果是no,用户必须手动redis-check-aof修复AOF文件才可以。
参考自https://www.cnblogs.com/pqchao/p/6558688.html
缺点就是最后一次执行的rdb文件可能会丢失。
如果想恢复rdb文件,,只要把.rdb的文件格式放到对应的目录下就可以了,redis在启动的时候会检查恢复数据
如果aof文件有问题,那么redis是启动不了的。
[wdd@localhost src]$ redis-check-aof --fix appeeee.aof // 修复名为appeeee.aof的aof文件
Redis消息订阅
redis主从复制
将一台redis服务器上的数据,转移到另外n台服务器上。数据只能由主服务器到从服务器,不能反过来。
主从复制的作用:
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、故障恢复∶当主节点挂了,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
3、负载均衡∶在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接
主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4、高可用(集群)基础∶主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
redis 只有主机能写,从机只能读
127.0.0.1:6379> SLAVEOF no one // 把自己弄成主机 篡位。当原先的主机恢复了,原先的还是主机,只不过没用了
OK
主服务器宕机后,哨兵不会马上去选举,只有当一定数量的哨兵都认为服务器宕机了,才会开始选举。选举完后,首先会进行故障转移操作,然后新的主机会通过发布订阅模式,让哨兵们吧自己从从节点转换成主机监控。
哨兵模式的特点:如果在选举途中,原来的主服务器突然好了,那么这个服务器只能当新的主服务器的从机。
类似秦昭襄王挟持楚怀王,就算楚怀王跑了,也只能乖乖的当新王的臣下
哨兵的配置文件和普通redis的config是差不多的。
注意:redis只有一主多从,才有哨兵的选举。虽然一主一从/多主多从 官网说是选举,不过都是直接给从节点的,个人认为这种世袭制可不叫选举!
集群模式
Redis Cluster 是 3.0 版后推出的 Redis 分布式集群解决方案,主要解决 Redis 分布式方面的需求,比如,当遇到单机内存,并发和流量等瓶颈的时候,Redis Cluster 能起到很好的负载均衡的目的。Redis Cluster 集群节点最小配置 6 个节点以上(3 主 3 从),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。Redis Cluster 采用虚拟槽分区,所有的键根据哈希函数映射到 0~16383 个整数槽内,每个节点负责维护一部分槽以及槽所印映射的键值数据。
Redis 的几种常见使用方式包括:
单机模式
主从模式
哨兵模式(sentinel)
集群模式(cluster)
第三方模式
单机模式
优点:1.部署简单,一个文件就搞定
2.高性能
3.省。不用拿备用的子节点来。
缺点:自己死了就全部死了,断后。新数据全部丢失。
主从模式
优点:1.高可靠。有故障的时候自动主从切换。
2.主从分离
缺点:1.主机出故障了,要手动切换主节点
2.原生复制的弊端在早期的版本中也会比较突出,如:Redis 复制中断后,Slave 会发起 psync,此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时可能会造成毫秒或秒级的卡顿;又由于 COW 机制,导致极端情况下的主库内存溢出,程序异常退出或宕机;主库节点生成备份文件导致服务器磁盘 IO 和 CPU(压缩)资源消耗;发送数 GB 大小的备份文件导致服务器出口带宽暴增,阻塞请求,建议升级到最新版本。
哨兵模式
优点:1.省去了手动切换新的主节点
2.哨兵弄集群,单个哨兵死了不会影响特别大,保持系统稳健性
3.主从切换,故障也转移了,可用性up+++
缺点:1.因为用了自动的哨兵,所以在线手动扩容就比较麻烦
2.哨兵配置杂七杂八。
3.资源浪费,Redis 数据节点中 slave 节点作为备份节点不提供服务。
集群模式
优点:
没有中心结构–》即主节点
数据按照 哈希槽 存储分布在多个节点,节点间数据共享,可动态调整数据分布。即分布式存储
可扩展性:可线性扩展到 1000 多个节点,节点可动态添加或删除。
高可用性:部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升。
降低运维成本,提高系统的扩展性和可用性。
缺点:
Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。目前仅 JedisCluster 相对成熟,异常处理部分还不完善,比如常见的“max redirect exception”。
节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout),被判断下线,这种 failover (故障转移)是没有必要的。
数据通过异步复制,不保证数据的强一致性。
多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容易出现相互影响的情况。
Slave 在集群中充当“冷备”,只是拿来做备份的,不能缓解读压力,当然可以通过 SDK 的合理设计来提高 Slave 资源的利用率。
Key 批量操作限制,如使用 mset、mget 目前只支持具有相同 slot 值的 Key 执行批量操作。对于映射为不同 slot 值的 Key 由于 Keys 不支持跨 slot 查询,所以执行 mset、mget、sunion 等操作支持不友好。
Key 事务操作支持有限,只支持多 key 在同一节点上的事务操作,当多个 Key 分布于不同的节点上时无法使用事务功能。
Key 作为数据分区的最小粒度,不能将一个很大的键值对象如 hash、list 等映射到不同的节点。
不支持多数据库空间,单机下的 redis 可以支持到 16 个数据库,集群模式下只能使用 1 个数据库空间,即 db 0。
复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。
避免产生 hot-key,导致主库节点成为系统的短板。
避免产生 big-key,导致网卡撑爆、慢查询等。
重试时间应该大于 cluster-node-time 时间。
Redis Cluster 不建议使用 pipeline 和 multi-keys 操作,减少 max redirect 产生的场景。