redis
将存储库添加到APT索引,更新它,并安装Redis:
在 Ubuntu/Debian 上安装
将存储库添加到APT索引,更新它,并安装Redis:
sudo apt-get install lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
#这条是关键
sudo apt-get install redisRedis将自动启动,它将在启动时重新启动。
在后台开始和停止Redis
你可以使用 systemctl 命令将Redis服务器作为后台进程启动。
sudo systemctl start redis要停止服务器,使用:
sudo systemctl stop redis连接到Redis
使用 redis-cli连接到redis:
redis-cli使用 ping 命令测试连接:
127.0.0.1:6379> ping
PONG后台运行
在配置文件中添加下面这条允许启动方式是后台启动
daemonize yes启动命令
redis-server [配置文件路径]关闭
maluyao@MaLuYaoUbuntu:~$ redis-cli
127.0.0.1:6379> shutdown#添加键值对
127.0.0.1:6379> set name zhangsan
OK
#获取键值对
127.0.0.1:6379> get name
"zhangsan"
#查看所有key
127.0.0.1:6379> keys *
1) "key:__rand_int__"
2) "myhash"
3) "mystream"
4) "counter:__rand_int__"
5) "mylist"
6) "name"
#切换数据库(默认有16个数据库)
127.0.0.1:6379> select 1
OK
#显示数据库大小
127.0.0.1:6379[1]> dbsize
(integer) 0
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> dbsize
(integer) 6
#清空
flushall#清空全部
flushdb#清空当前数据库
# 查看是否存在key
127.0.0.1:6379> exists name
(integer) 1
#移动key
move key db
#设置key的过期时间
127.0.0.1:6379> expire key seconds
#ttl查看过期时间
127.0.0.1:6379> set name 2
OK
127.0.0.1:6379> expire name 10
(integer) 1
127.0.0.1:6379> ttl name
(integer) 8
...
(integer) 1
127.0.0.1:6379> ttl name
(integer) 0
127.0.0.1:6379> ttl name
(integer) 0
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> ttl name
(integer) -2
#查看key类型
127.0.0.1:6379> type key
向key的值中追加字符串,如果key不存在就相当于set key
append key value
获取字符串长度
strlen key
数值增加
incr key
数值减少
DECR key
增加N
INCRBY key increment
减少N
DECRBY key decrement
#取固定范围的字符串
getrange key start end
#替换指定位置开始的字符串
setrange key offset value
(set with expire 设置过期时间)
setex key seconds value
(set if not exist不存在设置)
setnx key value
批量设置或者获取
mget key [key ...]
mset key value [key value ...]
不存在创建
msetnx key value [key value ...]
#这是一个原子性操作,一个设置不存在就全都不设置
#对象
#先get后set
getset key valueJedis是Java官方提供的操作 Redis的工具
命令
五大数据类型
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
Redis-Key
#添加键值对
set name zhangsan
OK
#获取键值对
get name
#查看所有key
keys *
# 查看是否存在key
127.0.0.1:6379> exists name
#移动key
move key db
move name 1#把name移动到数据库1中
#设置key的过期时间
127.0.0.1:6379> expire key seconds
#ttl查看过期时间
127.0.0.1:6379> ttl name
(integer) 8
type key#查看当前key的类型
String(字符串)
追加字符串
append key value#当前key不存在就相当于set key
字符串长度
strlen key数值:
数值增加
incr key
数值减少
DECR key
增加N
INCRBY key increment
减少N
DECRBY key decrement字符串操作
#取固定范围的字符串
getrange key start end
127.0.0.1:6379[1]> set name 123456789
127.0.0.1:6379[1]> getrange name 0 1 #获取字符串[0,1]
"12"
127.0.0.1:6379[1]> getrange name 0 -1
"123456789"
#替换指定位置开始的字符串
setrange key offset value
127.0.0.1:6379[1]> set name 123456789
OK
127.0.0.1:6379[1]> setrange name 0 xx
(integer) 9
127.0.0.1:6379[1]> get name
"xx3456789"
#(set with expire)
setex key seconds value
#不存在的时候设置(set if not exist)
setnx批量设置或者获取
mget key [key ...]
mset key value [key value ...]
不存在创建
msetnx key value [key value ...]#先get后set
getset key valueList
是一种基本的数据类型
在redis中可以把list玩成栈、队列、阻塞队列!
所有List命令都是以L/R开头,(L就是Left,R就是Right)
添加
lpush key element [element ...]
获取
lrange key start stop
127.0.0.1:6379> lrange name 0 -1
1) "lisi"
2) "zhangsan"
lpop
rpop
#通过下标获取值
lindex key index
#返回列表长度
llen
#移除 指定 的值
lrem key count element
count就是移除多少个重复的值
只保留一定范围的key
trim 修剪
ltrim key start stop移动列表
rpoplpush source destination
从原list右边pop一个元素,给新list左边push进去lset key index element插入值
linsert key BEFORE|AFTER pivot element
pivot-->源字符串
127.0.0.1:6379> lrange name 0 -1
1) "wangwu"
2) "lisi"
127.0.0.1:6379> linsert name after "wangwu" zhangsan
(integer) 3
127.0.0.1:6379> lrange name 0 -1
1) "wangwu"
2) "zhangsan"
3) "lisi"Set(集合)
set中的值是不能重读的!
set的操作都是S开头
#添加与查看
sadd key member [member ...]
smembers key
127.0.0.1:6379> sadd myset hello
(integer) 1
127.0.0.1:6379> smembers myset
1) "hello"
#判断是否包含
SISMEMBER key member
#元数个数
scard key
#删除值
srem key member [member ...]set是无序不重复集合,抽随机!
SRANDMEMBER key [count]
127.0.0.1:6379> sadd numbers 1 2 3 4 5
(integer) 5
127.0.0.1:6379> SMEMBERS numbers
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> SRANDMEMBER numbers 2
1) "1"
2) "4"随机删除一些元素
spop key [count]
127.0.0.1:6379> SMEMBERS numbers
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
127.0.0.1:6379> spop numbers
"5"
127.0.0.1:6379> spop numbers
"4"
127.0.0.1:6379> SMEMBERS numbers
1) "1"
2) "2"
3) "3"将一个指定的值移动到另外一个set集合中
smove source destination member
127.0.0.1:6379> sadd myset1 1 2 3 4 5 6
(integer) 6
127.0.0.1:6379> smove myset1 myset2 1
(integer) 1
127.0.0.1:6379> SMEMBERS myset1
1) "2"
2) "3"
3) "4"
4) "5"
5) "6"
127.0.0.1:6379> SMEMBERS myset2
1) "1"集合操作
差集、交集、并集
127.0.0.1:6379> SMEMBERS set2
1) "c"
2) "e"
3) "d"
4) "f"
127.0.0.1:6379> SMEMBERS set1
1) "d"
2) "c"
3) "b"
4) "a"
#差集
127.0.0.1:6379> sdiff set1 set2
1) "a"
2) "b"
#并集
127.0.0.1:6379> sunion set1 set2
1) "c"
2) "b"
3) "a"
4) "e"
5) "d"
6) "f"
#交集
127.0.0.1:6379> sinter set1 set2
1) "d"
2) "c"Hash(哈希)
Map集合,key-value —> key-map
也就是说一个key对应的是一个map
hash指令是以h开头的
#设置值
hset key field value [field value ...]
#同时设置多个值(好像和上面是一样的)
hmset key field value [field value ...]
127.0.0.1:6379> hset myhash name zhangsan
(integer) 1
127.0.0.1:6379> hset myhash age 18
(integer) 1
#获取值
hget key field
hmget key field [field ...]
hgetall key
#获取全部
127.0.0.1:6379> hgetall myhash
1) "name"
2) "zhangsan"
3) "age"
4) "18"删除值:
hdel key field [field ...]获取长度(字段数量)
hlen
127.0.0.1:6379> hlen myhash
(integer) 2判断存在
HEXISTS key field只获得所有的FIELD或者VALUE
127.0.0.1:6379> hkeys myhash #只获得所有的field
1) "name"
2) "age"
127.0.0.1:6379> hvals myhash #只获得所有的value
1) "zhangsan"
2) "18"增加减少
hincrby key field increment
没有hdecrby,可以添加一个负数作为减少还有存在条件
hsetnx key field value
不存在才创建hash适用于做用户信息的储存,String适合字符串存储
hset user:1 name zhangsan age 18
Zset(有序集合)
在set基础上,增加一个值
set k1 v1 ---> set k1 score v1
zadd key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]
127.0.0.1:6379> zadd myzset 1 one 2 two #添加多个值
(integer) 2
#排序如何实现
添加一点:
127.0.0.1:6379> zadd salary 1000 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 4000 lisi
(integer) 1
#查询
zrange key start stop [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]
#排序
zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
127.0.0.1:6379> zrangebyscore salary -inf +inf #默认是升序从小到大
1) "zhangsan"
2) "lisi"
#逆序排序
ZREVRANGEBYSCORE salary +inf -inf
127.0.0.1:6379> zrevrangebyscore salary +inf -inf withscores
1) "lisi"
2) "4000"
3) "zhangsan"
4) "1000"
5) "wuming"
6) "100"删除
zrem key member [member ...]查看数量
zcard key
#范围数量
zcount key min max
127.0.0.1:6379> zcount salary 00 200
(integer) 1三种特殊的数据类型
geospatial地理位置空间
Redis的Geo在Redis3.2版本就推出了!这人功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
一共只有六个命令:
GEOADD 命令以标准的 x,y 格式接受参数, 所以用户必须先输入经度, 然后再输入纬度。 GEOADD 能够记录的坐标是有限的:非常接近两极的区域是无法被索引的。
说明: 没有 GEODEL 命令,因为可以使用 ZREM 来删除元素。Geo 底层的索引结构是 sorted set 。
显示可以使用zrange展示
127.0.0.1:6379> zrange china:city 0 -1
1) "hefei"
2) "beijin"127.0.0.1:6379> geoadd china:city 116.40528 39.904989 beijin
(integer) 1
一般是下载地图数据,直接通过Java程序一次性导入Redis GEOPOS 命令用于从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。
127.0.0.1:6379> geoadd china:city 117.283042 31.86119 hefei
(integer) 1
127.0.0.1:6379> geopos china:city hefei
1) 1) "117.28304117918014526"
2) "31.86119023341409928"GEODIST 命令用于返回两个给定位置之间的距离。
如果两个位置之间的其中一个不存在, 那么命令返回空值。
127.0.0.1:6379> geodist china:city beijin hefei
"898157.1673"
geodist china:city beijin hefei [M|KM|FT|MI]
georadius 命令以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
georadius key longitude latitude radius M|KM|FT|MIgeoradiusbymember和 GEORADIUS 命令一样, 都可以找出位于指定范围内的元素, 但是 georadiusbymember 的中心点是由给定的位置元素决定的, 而不是使用经度和纬度来决定中心点。
georadiusbymember key member radius M|KM|FT|MI
完整的写法:
georadiusbymember key member radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key] [STOREDIST key]
限定数量
count 2geohash 用于返回一个或多个位置元素的 Geohash 表示。Redis GEO 使用 geohash 来保存地理位置的坐标。
127.0.0.1:6379> geohash china:city hefei
1) "wtemkb4uuu0"备注
geospatial底层就是zset,可以使用zset命令来操作geospatial
127.0.0.1:6379> zrange china:city 0 -1 withscores
1) "hefei"
2) "4052764742757487"
3) "beijin"
4) "4069885370671008"hyperloglog基数统计
什么是基数?
基数就是不重复的元素
A{1,3,5,7,8,7}
B{1,3,5,7,8}
基数(不重复的元素)=5,可以接受误差
简介
redis2.8.9版本更新了hyperloglog数据结构
redis hyperloglog是做统计的算法
网页的UV(一个人访问一个网站多次,但是还是算作一个人!) 传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户d,就会比较麻烦!我们的目的是计数而不是统计用户ID!!!
优点:占用内存固定。$2^{64}$不同的元素的技术,只需要废12KB内存!如果要从内存角度来比较的话Hyperloglog首选!
有0.81%的错误率。
所有的hyperloglog命令是以P开头的
#添加key
127.0.0.1:6379> PFadd mykey a b c d e f
(integer) 1
#统计基数
127.0.0.1:6379> PFcount mykey
(integer) 6
127.0.0.1:6379> PFadd mykey2 e f g h i j
(integer) 1
127.0.0.1:6379> PFcount mykey2
(integer) 6
#合并两组
127.0.0.1:6379> PFmerge mykey mykey2
OK
127.0.0.1:6379> PFcount mykey
(integer) 10如果允许容错,一定要使用hyperloglog
bitmaps位图
位存储,使用位标记01000001
存储状态
两个状态的都可以使用bitmap
setbit key offset value
127.0.0.1:6379> setbit sign 0 0 #设置第一位为0
(integer) 0
127.0.0.1:6379> setbit sign 1 0 #设置第二位为0
(integer) 0
127.0.0.1:6379> setbit sign 2 1 #设置第三位为1
(integer) 0
#获取数据
getbit key offset
127.0.0.1:6379> getbit sign 0
(integer) 0
#统计操作
bitcount key [start end [BYTE|BIT]]
127.0.0.1:6379> bitcount sign
(integer) 1事务
Redis单条命令式保存原子性的,但是事务不保证原子性
Redis事务没有没有隔离级别的概念!
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程的中,会按照顺序执行!
Redis 事务允许在单个步骤中执行一组命令,主要围绕 MULTI、EXEC、DISCARD 和 WATCH 命令展开。Redis 事务提供两个重要保证:
redis的事务:
- 开启事务(
multi) - 命令入队()
- 执行事务()
#开启事务
127.0.0.1:6379> MULTI
OK
#命令入队
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
#执行事务
127.0.0.1:6379(TX)> exec
1) OK
2) OK放弃事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> DISCARD
OK编译型异常(代码有问题!命令有错!),事务中所有的命令都不会被执行!
运行时异常(如果事务队列中存在语法性,那么执行命令的时候,其他命令式可以正常执行的)
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) (error) ERR value is not an integer or out of range监控实现乐观锁
悲观锁: 很悲观,认为什么时候都会出问题,无论做什么都会加锁! 乐观锁: 很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据,version !
Redis WATCH 命令用于标记要监视的key,以便有条件地执行事务(更多参考transaction)。
监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,所以在MULTI命令后可以修改WATCH监控的键值)
WATCH 用于为 Redis 事务提供检查-设置(CAS)行为。
被 WATCH 的键会被监控以检测其变化。如果在调用 EXEC 之前,至少有一个被监控的键被修改,则整个事务会中止,并且 EXEC 返回 空值回复 以通知事务失败。
测试多线程修改值,使用watch可以当做redis的乐观锁操作!
#监视值
127.0.0.1:6379> watch a
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set a 10
QUEUED
127.0.0.1:6379(TX)> incr a
QUEUED
127.0.0.1:6379(TX)> exec
(nil)Redis UNWATCH命令用于取消 WATCH 命令对所有 key 的监视。
如果执行过 EXEC 或DISCARD,无需再执行 UNWATCH。
因为 EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD命令在取消事务的同时也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行 UNWATCH了。
Jedis
SpringBoot整合
Redis.conf详解
Redis持久化
Redis发布订阅
Redis主从复制
Redis缓存穿透和雪崩
1.缓存:
-
缓存穿透:查询一个不存在的数据,mysql查询不要也不会直接写入缓存导致每次请求都去访问数据库
解决方法:1.缓存空数据,查询返回的数据为空,仍然把这个空结果缓存进数据库,简单,消耗内存,容易导致数据不一致性
2.布隆过滤器:缓存预热时也要预热布隆过滤器,先去查询布隆过滤器的key,如果key不存在就直接方法,
内存占用少,没有多余key,但存在误判
-
缓存击穿: 给某一个key设置了过期数据,当key过期的时候,恰好这个时间点有大量并发请求过来,这些并发请求有可能压塌数据库
解决方法:
使用互斥锁:当缓存失效时,不立即去load db,先使用如 Redis 的
setnx 去设置一个互斥锁,当操作成功返回时再进行 load db的操作并回设缓
存,否则重试get缓存的方法。保证了数据的一致性但分布式锁容易导致死锁问题
key逻辑过期:在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间
当查询的时候,从redis取出数据后判断时间是否过期如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新。高可用性,性能比较高,但是数据同步这块做不到强一致。
创建定时任务对一个热点key设置一个定时任务,在key快要过期时,定时任务自动刷新key
-
缓存雪崩:是指同一时间内大量的缓存key同时失效或者redis噶了,导致大量请求到底数据库,造成压力
解决方法:给不同的设置各种的过期时间,设置一个定时任务,定时循环刷新热点key主动打散热点key
2.双写一致性:
-
双写一致性:当修改数据库的数据时也要同时更新缓存里面的数据,缓存和数据库里面的数据保持一致性
-
读操作:缓存命中,直接返回,返回未命中查询数据库,写入缓存,设置超时时间
-
允许延时一致业务:使用MQ消息中间件,更新数据后,通知缓存删除
-
对于强一致性业务:采用redisson提供的读写锁
共享锁即读锁readlock:加锁以后,其他线程可以操作读数据
排它锁:独占锁即writelock,加锁以后,阻塞其他线程进行读写操作
3.延迟双删:
- 延迟双删(Delayed Double Deletion)是一个在读写高并发场景下,确保缓存与数据库数据一致性的一种策略。它的核心思想是通过两次删除缓存操作以及适当的延迟,来避免缓存和数据库间的数据不一致问题
- 更新数据库:首先,更新数据库中的数据。这是为了确保数据的准确性。
- 删除缓存:在更新数据库后,立即删除相关的缓存。这一步是为了防止旧数据继续存在于缓存中。
- 延迟一段时间:等待一段时间(通常是比业务操作预计完成的时间稍长一些),目的是确保所有可能的缓存重建请求已经结束。
- 再次删除缓存:再次删除缓存,以确保在第一步删除缓存和更新数据库之间可能产生的缓存重建已被清除。
4.redis数据的持久化:
- RDB:定时对整个内存做快照,数据并不完整,2次备份之间会丢失,对于ROB来讲会进行文件压缩,空间占用较小,但回复数据很快,但数据恢复优先级较低。因为数据完整性不如AOF,占用大量的cpu和内存消耗,适用场景:可以容忍数分钟的数据丢失,追求更快的启动速度
- AOF:记录每一次的执行命令,数据先对完整,主要取决于刷盘策略,因为记录命令,所以1文件体积先对较大恢复速度较慢,当数据完整性更高,恢复优先级较高, 系统资源占用较低,主要是磁盘io资源,但AOF重写时会占用大量的cpu和内存资源对数据的安全性较高时使用
5.数据过期策略:
-
redis对数据设置数据的有效时间,数据过期以后,就需要把数据从内存中删除,可以按照不同的规则进行删除,这个规则就是数据过期策略
-
惰性删除:设置过期时间后,不许要对他自动删除,只会在需要这个key时,会检查是否过期,如果过期了就自动删除,否则就返回这个key,但对内存占用较大,如果这个key过期了但一直没有使用就会占用内存
-
定期删除:每隔一段时间就会对一些key进行检查,删除里面过期的key,最终检测所有的key,可以通过限制删除操作执行的时长和频率来减少删除操作对cpu的影响,定期删除也有效释放过期key占用的内存,缺点是难以限制删除操作执行的时长和频率
定期删除有两种模式:
- SLOW:是定时任务,执行频率默认为10hz,每次不超过25ms,可以通过redis.conf的hz选项进行修改
- FAST:执行频率不固定,但2次间隔不低于2ms,2次耗时不超过1ms
6.数据淘汰策略:
- 当缓存内存不足时,redis就会按照某种规则将内存中的数据删除,这种规则就是数据淘汰策略
- noevction: redis默认策略,不淘汰任何key,但内存不足时不允许写入任何数据
- volatile-ttl: 对设置了ttl(过期时间)的key,比较key剩余的ttl值,ttl越小越容易被淘汰
- allkeys-random: 对全体key随机进行淘汰
- volatile-random: 对设置了ttl的key进行随机淘汰
- allkeys-lru: 对全体key,基于LRU算法进行淘汰
- volatile-lfu:对设置了ttl的key基于LRU算法进行淘汰
- allkeys-lfu:对全体key,基于LFU算法进行淘汰
- volatile-lfu:对全体设置ttl的key,基于LFU算法进行淘汰
- LRU:最近最少使用,用当前时间减去最后访问时间,值越大淘汰优先级越高
- LFU:最少频率最低,统计每个key的访问频率,值越小,淘汰优先级越高
7.分布式锁:
-
redis实现分布式锁主要利用setnx,即set if not exists,因为redis是单线程的,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或删除key的时候是其他客户端是不能设置这个key的,同时redis底层是lua脚本,保证原子性
-
在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成的时候,在redisson中引入了一个看门狗机制,就是说每隔一段时间就检查当前业务是否还持有锁,如果持有就增加加锁的持有时间,当业务执行完成之后需要使用释放锁就可以了,正常看门狗锁刷新时间为过期时间/3,默认为10
-
redis的分布式锁可以重入:这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上减一。在存储数据的时候采用的hash结构,大key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数
-
redisson这个锁解决不了主重一致性,可以引入红锁,但红锁性能太低了。redisson提倡高可用,这时候建议使用zookeeper实现的分布式锁
8.redis集群方案:
- 主从复制:单个redis的并发能力有上线,要提高redis的并发能力,就需要搭建主从集群(一主多从),实现读写分离
一般情况下:都由主节点master进行写的操作,多个从节点slave进行读操作,需要主从数据同步
-
主从数据同步原理:
-
主从全量同步: 从节点请求主节点同步数据(replication id ,offset)
主节点判断是否为第一次请求,如果为第一次就与从节点同步版本号
主节点会执行bgsave,生成rdb文件,发送给我从节点进行数据同步,
同时在rdb期间也会接收其他命令并写入一个日志文件
然后把生成的日志文件发给从节点进行数据同步
-
增量同步:从节点请求同步主节点数据,如果不是第一次请求就获取从节点的offset,
主节点同步命令从日志中获取offset值之后的日志,发送给从节点进行数据同步
-
9.哨兵模式:
-
redis提供了哨兵机制来实现主从集群的自动故障恢复能力
- 监控:会不断的检测master和slave是否会正常工作
- 自动故障升级:如果master故障后,哨兵会自动将一个slave升级为master,当实例故障恢复后也以新的master为主
- 通知;哨兵充当redis客户端的服务发现来源,当集群发生故障时,会将最新的消息推送给redis客户端
- 选举规则:哨兵配置文件中的优先级,复制偏移量,被选中的次数(被选中次数),连接状态和响应时间
-
脑裂:
- 由于redis master节点和redis salve节点和sentinel处于不同的网络分区,使得sentinel没有能够心跳感知到master,所以通过选举的方式提升了一个salve为master,这样就存在了两个master,就像大脑分裂了一样,这样会导致客户端还在old master那里写入数据,新节点无法同步数据,当网络恢复后,sentinel会 将old master降为salve,这时再从新master同步数据,这会导致old master中的大量数据丢失
解决方法:
- 第一可以设置最少的salve节点个数,比如设置至少要有一个从节点才能同步数据,
- 第二个可以设置主从数据复制和同步的延迟时间,达不到要求就拒绝请求,就可以避免大量的数据丢失
10.分片集群:
-
分片集群作用:
- 集群中有多个master,每个master保存不同的数据
- 每个master有多个slave节点
- master之间通过ping检测彼此健康
- 客户端可以访问集群中的任一节点,最终请求都会被转发到正确的节点
-
redis分片集群中数据怎么存储和读取的
- redis分片集群引入hash槽的概念,redis集群中有16384个哈希槽
- 将163384分配给不同的实例
- 每个请求过来会对key有效部分进行取模,就通过key的哈希值对16384进行取模,余数作为插槽,寻找插槽所在的实例
10.redis是单线程为什么这么快:
-
redis是纯内存操作,执行速度非常快
-
采用单线程操作,避免了不必要的上下文切换可竞争条件,多线程还要考虑线程安全问题
-
使用I/O多路复用模型,非阻塞io,基本上采用epoll
-
redis是纯内存操作,执行速度非常快,他的性能瓶颈是网络延迟而不是执行速度,I/O多路复用模型
-
I/O多路复用;利用单线程去监听多个socket,并在莫个socket可读,可写时,得到通知,从而避免了无效等待充分利用了CPU资源
-
几种监听模式:
-
select:自会通知用户进程有scoket就绪了,但不确定到低是哪个socket,需要用户遍历所有scoket来确定
-
poll: 同select同理
-
epoll :在通知用户进程就绪的同时,把已经就绪的scoket写入内存中
-
-
-
redis6.0引用了多线程,但本质是还是单线程操作,只不过命令解析,和写出数据使用了多线程,但命令执行还是单线程,找不到多线程解决了网络问题,即命令回复处理器和命令应答处理器采用了多线程
评论
评论加载中……