Redis 介绍
Redis 是一个高性能的 key-value 数据库。每秒可执行操作高达 10万+ QPS
Redis 特点
- 支持数据持久化,可将内存中的数据保存在磁盘,重启时再次加载
- 支持 KV 类型数据,也支持其他丰富的数据结构存储
- 支持数据备份,即 master-slave 模式的数据备份
常用命令
- exists key:判断键是否存在
- del key:删除键值对
- move key db:将键值对移动到指定数据库
- expire key second:设置键值对的过期时间
- type key:查看value的数据类型
127.0.0.1:6379> move age 1 # 将键值对移动到指定数据库
(integer) 1
127.0.0.1:6379> set age 20
OK
127.0.0.1:6379> EXPIRE age 15 # 设置键值对的过期时间
(integer) 1 # 设置成功 开始计数
127.0.0.1:6379> ttl age # 查看key的过期剩余时间
(integer) 13 # -2 表示key过期,-1表示key未设置过期时间
- 性能测试
[root@instance-6fuvnygy redis]# ./redis-benchmark --help
Usage: redis-benchmark [-h <host>] [-p <port>] [-c <clients>] [-n <requests>] [-k <boolean>]
-h <hostname> Server hostname (default 127.0.0.1)
-p <port> Server port (default 6379)
-s <socket> Server socket (overrides host and port)
-a <password> Password for Redis Auth
-c <clients> Number of parallel connections (default 50)
-n <requests> Total number of requests (default 100000)
-d <size> Data size of SET/GET value in bytes (default 3)
--dbnum <db> SELECT the specified db number (default 0)
-k <boolean> 1=keep alive 0=reconnect (default 1)
-r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD
Using this option the benchmark will expand the string __rand_int__
inside an argument with a 12 digits number in the specified range
from 0 to keyspacelen-1. The substitution changes every time a command
is executed. Default tests use this to hit random keys in the
specified range.
-P <numreq> Pipeline <numreq> requests. Default 1 (no pipeline).
-e If server replies with errors, show them on stdout.
(no more than 1 error per second is displayed)
-q Quiet. Just show query/sec values
--csv Output in CSV format
-l Loop. Run the tests forever
-t <tests> Only run the comma separated list of tests. The test
names are the same as the ones produced as output.
-I Idle mode. Just open N idle connections and wait.
# 举例
[root@instance-6fuvnygy redis]# ./redis-benchmark # 默认50个客户端 请求100000次 写入长度3bytes
====== SET ======
100000 requests completed in 1.13 seconds # 100000次请求完成时间
50 parallel clients # 50个并行客户端
3 bytes payload #
keep alive: 1
99.97% <= 1 milliseconds
100.00% <= 1 milliseconds
88495.58 requests per second
====== GET ======
100000 requests completed in 1.14 seconds
50 parallel clients
3 bytes payload
keep alive: 1
99.93% <= 1 milliseconds
100.00% <= 1 milliseconds
87412.59 requests per second
- 查看redis库信息:info keyspace
172.38.0.16:6379> info keyspace
# Keyspace
db0:keys=2,expires=0,avg_ttl=0
- flushdb---清除当前数据库的所有keys
- flushall---清除所有数据库的所有keys,
- redis中文显示问题,用客户端连接redis服务器时,加上 --raw 选项
172.38.0.16:6379> set name 张三
OK
172.38.0.16:6379> get name
"\xe5\xbc\xa0\xe4\xb8\x89\x89"
172.38.0.16:6379>
172.38.0.16:6379>
172.38.0.16:6379>
[root@instance-6fuvnygy ~]# /yisa_oe/redis/redis-cli -c -p 6001 --raw
127.0.0.1:6001> get name
-> Redirected to slot [5798] located at 172.38.0.16:6379
张三
- sock 连接方式 sock连接 ./redis-cli -s /dev/shm/redis.sock
5种数据结构
1.STRING:字符串、整数或浮点数
| 命令 | 描述 |
|---|---|
| APPEND key value | 向指定的key的value后追加字符串 |
| DECR/INCR key | 将指定key的value数值进行+1/-1(仅对于数字) |
| INCRBY/DECRBY key n | 按指定的步长对数值进行加减 |
| INCRBYFLOAT key n | 为数值加上浮点型数值 |
| STRLEN key | 获取key保存值的字符串长度 |
| GETRANGE key start end | 按起止位置获取字符串(闭区间,起止位置都取) |
| SETRANGE key offset value | 用指定的value 替换key中 offset开始的值 |
| GETSET key value | 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 |
| SETNX key value | 仅当key不存在时进行set |
2. LIST(列表)
一个列表最多可以包含 2^32 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。首先我们列表,可以经过规则定义将其变为队列、栈、双端队列等
| 命令 | 描述 |
|---|---|
| LPUSH/RPUSH key value1 | 从左边/右边向列表中PUSH值(一个或者多个)。 |
| LRANGE key start end | 获取list 起止元素*(索引从左往右 递增)* |
| LPUSHX/RPUSHX key value | 向已存在的列名中push值(一个或者多个) |
| LINSERT key BEFORE AFTER pivot value | |
| LLEN key | 查看列表长度 |
| LINDEX key index | 通过索引获取列表元素 |
| LSET key index value | 通过索引为元素设值 |
| LPOP/RPOP key | 从最左边/最右边移除值 并返回 |
| RPOPLPUSH source destination | 将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部 |
| LTRIM key start end | 通过下标截取指定范围内的列表 |
| LREM key count value | List中是允许value重复的 count > 0:从头部开始搜索 然后删除指定的value 至多删除count个 count < 0:从尾部开始搜索… count = 0:删除列表中所有的指定value。 |
| BLPOP/BRPOP key1[key2] timout | 移出并获取列表的第一个/最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
| BRPOPLPUSH source destination timeout | 和RPOPLPUSH功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
3. SET(集合)
Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
集合中最大的成员数为 x^32-1 (4294967295, 每个集合可存储40多亿个成员)。
| 命令 | 描述 |
|---|---|
| SADD key member1[member2..] | 向集合中无序增加一个/多个成员 |
| SCARD key | 获取集合的成员数 |
| SMEMBERS key | 返回集合中所有的成员 |
| SISMEMBER key member | 查询member元素是否是集合的成员,结果是无序的 |
| SRANDMEMBER key [count] | 随机返回集合中count个成员,count缺省值为1 |
| SPOP key [count] | 随机移除并返回集合中count个成员,count缺省值为1 |
| SMOVE source destination member | 将source集合的成员member移动到destination集合 |
| SREM key member1[member2..] | 移除集合中一个/多个成员 |
| SDIFF key1[key2..] | 返回所有集合的差集 key1- key2 - … |
| SDIFFSTORE destination key1[key2..] | 在SDIFF的基础上,将结果保存到集合中*(覆盖)*。不能保存到其他类型key噢! |
| SINTER key1 [key2..] | 返回所有集合的交集 |
| SINTERSTORE destination key1[key2..] | 在SINTER的基础上,存储结果到集合中。覆盖 |
| SUNION key1 [key2..] | 返回所有集合的并集 |
| SUNIONSTORE destination key1 [key2..] | 在SUNION的基础上,存储结果到及和张。覆盖 |
| SSCAN KEY [MATCH pattern] [COUNT count] | 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分 |
4. HASH(哈希)
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Set就是一种简化的Hash,只变动key,而value使用默认值填充。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。
| 命令 | 描述 |
|---|---|
| HSET key field value | 将哈希表 key 中的字段 field 的值设为 value 。重复设置同一个field会覆盖,返回0 |
| HMSET key field1 value1 [field2 value2..] | 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
| HSETNX key field value | 只有在字段 field 不存在时,设置哈希表字段的值。 |
| HEXISTS key field | 查看哈希表 key 中,指定的字段是否存在。 |
| HGET key field value | 获取存储在哈希表中指定字段的值 |
| HMGET key field1 [field2..] | 获取所有给定字段的值 |
| HGETALL key | 获取在哈希表key 的所有字段和值 |
| HKEYS key | 获取哈希表key中所有的字段 |
| HLEN key | 获取哈希表中字段的数量 |
| HVALS key | 获取哈希表中所有值 |
| HDEL key field1 [field2..] | 删除哈希表key中一个/多个field字段 |
| HINCRBY key field n | 为哈希表 key 中的指定字段的整数值加上增量n,并返回增量后结果 一样只适用于整数型字段 |
| HINCRBYFLOAT key field n | 为哈希表 key 中的指定字段的浮点数值加上增量 n。 |
5.ZSET(有序集合)
不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的成员进行从小到大的排序。
score相同:按字典顺序排序
有序集合的成员是唯一的,但分数(score)却可以重复。
| 命令 | 描述 |
|---|---|
| ZADD key score member1 [score2 member2] | 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
| ZCARD key | 获取有序集合的成员数 |
| ZCOUNT key min max | 计算在有序集合中指定区间score的成员数 |
| ZINCRBY key n member | 有序集合中对指定成员的分数加上增量 n |
| ZSCORE key member | 返回有序集中,成员的分数值 |
| ZRANK key member | 返回有序集合中指定成员的索引 |
| ZRANGE key start end | 通过索引区间返回有序集合成指定区间内的成员 |
| ZRANGEBYLEX key min max | 通过字典区间返回有序集合的成员 |
| ZRANGEBYSCORE key min max | 通过分数返回有序集合指定区间内的成员*-inf 和 +inf分别表示最小最大值,只支持开区间()* |
| ZLEXCOUNT key min max | 在有序集合中计算指定字典区间内成员数量 |
| ZREM key member1 [member2..] | 移除有序集合中一个/多个成员 |
| ZREMRANGEBYLEX key min max | 移除有序集合中给定的字典区间的所有成员 |
| ZREMRANGEBYRANK key start stop | 移除有序集合中给定的排名区间的所有成员 |
| ZREMRANGEBYSCORE key min max | 移除有序集合中给定的分数区间的所有成员 |
| ZREVRANGE key start end | 返回有序集中指定区间内的成员,通过索引,分数从高到底 |
| ZREVRANGEBYSCORRE key max min | 返回有序集中指定分数区间内的成员,分数从高到低排序 |
| ZREVRANGEBYLEX key max min | 返回有序集中指定字典区间内的成员,按字典顺序倒序 |
| ZREVRANK key member | 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
| ZINTERSTORE destination numkeys key1 [key2 ..] | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中,numkeys:表示参与运算的集合数,将score相加作为结果的score |
| ZUNIONSTORE destination numkeys key1 [key2..] | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
3种特殊数据类型
1) Geo(地理位置)
使用经纬度定位地理坐标并用一个有序集合zset保存,所以zset命令也可以使用
有效的经度从-180度到180度 ; 有效的纬度从-85.05112878度到85.05112878度。
unit单位参数:
m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
| 命令 | 描述 | |
|---|---|---|
| geoadd key longitud(经度) latitude(纬度) member | 将具体经纬度的坐标存入一个有序集合 | |
| geopos key member [member..] | 获取集合中的一个/多个成员坐标 | |
| geodist key member1 member2 [unit] | 返回两个给定位置之间的距离。默认以米作为单位。 | |
| georadius key longitude latitude radius unit withcoord withdist | 通过georadius就可以完成 附近的人功能 radius : 半径 unit : 单位m | km... withcoord:带上坐标 withdist:带上距离,单位与半径单位相同COUNT n : 只显示前n个(按距离递增排序) |
| georadiusbymember key member radius unit withcoord withdist... | 功能与GEORADIUS相同,只是中心位置不是具体的经纬度, 而是使用结合中已有的成员作为中心点。 | |
| geohash key member1 [member2..] | 返回一个或多个位置元素的Geohash表示。 使用Geohash位置52点整数编码。 |
2) Hyperloglog(基数统计)
Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。
因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
其底层使用string数据类型
如果允许容错,那么一定可以使用Hyperloglog !
如果不允许容错,就使用set或者自己的数据类型即可 !
什么是基数?
数据集中不重复的元素的个数。
应用场景:
网页的访问量(UV):一个用户多次访问,也只能算作一个人。
传统实现,存储用户的id,然后每次进行比较。当用户变多之后这种方式及其浪费空间,而我们的目的只是计数,Hyperloglog就能帮助我们利用最小的空间完成。
| 命令 | 描述 |
|---|---|
| PFADD key element1 [elememt2..] | 添加指定元素到 HyperLogLog 中 |
| PFCOUNT key [key] | 返回给定 HyperLogLog 的基数估算值 |
| PFMERGE destkey sourcekey [sourcekey..] | 将多个 HyperLogLog 合并为一个 HyperLogLog |
----------PFADD--PFCOUNT---------------------
127.0.0.1:6379> PFADD myelemx a b c d e f g h i j k # 添加元素
(integer) 1
127.0.0.1:6379> type myelemx # hyperloglog底层使用String
string
127.0.0.1:6379> PFCOUNT myelemx # 估算myelemx的基数
(integer) 11
127.0.0.1:6379> PFADD myelemy i j k z m c b v p q s
(integer) 1
127.0.0.1:6379> PFCOUNT myelemy
(integer) 11
----------------PFMERGE-----------------------
127.0.0.1:6379> PFMERGE myelemz myelemx myelemy # 合并myelemx和myelemy 成为myelemz
OK
127.0.0.1:6379> PFCOUNT myelemz # 估算基数
(integer) 17
3) BitMaps(位图)
使用位存储,信息状态只有 0 和 1
Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。
| 命令 | 描述 |
|---|---|
| setbit key offset value | 为指定key的offset位设置值 |
| getbit key offset | 获取offset位的值 |
| bitcount key [start end] | 统计字符串被设置为1的bit数,也可以指定统计范围按字节 |
------------setbit--getbit--------------
127.0.0.1:6379> setbit sign 0 1 # 设置sign的第0位为 1
(integer) 0
127.0.0.1:6379> setbit sign 2 1 # 设置sign的第2位为 1 不设置默认 是0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> type sign
string
127.0.0.1:6379> getbit sign 2 # 获取第2位的数值
(integer) 1
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 4 # 未设置默认是0
(integer) 0
-----------bitcount----------------------------
127.0.0.1:6379> BITCOUNT sign # 统计sign中为1的位数
(integer) 4
redis签到功能实现思路
Redis提供了一组位操作相关的指令,这里我们关注下面三个:
BITCOUNT key [start end]
返回key的开始位置start到结束位置end之间位值为1的数量,如果key不存在,返回0;如果不指定start和end,返回整个key的位值为1的数量。
GETBIT key offset
返回key的第offset位的位值。
SETBIT key offset value
把key的第offset位的值设置为value,value只能是0或1。
说明一下Redis的位操作的偏移量(offset)是从0开始算起的,而且最左边那位是第0位,这与数值的二进制有点不同(数值的二进制最右边那位是第0位)。
事务
在 Redis 事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行,原子性支持不够好;使用 DISCARD 可以取消事务,放弃执行事务块内的所有命令
redis> MULTI #标记事务开始
OK
redis> INCR user_id #多条命令按顺序入队
QUEUED
redis> INCR user_id
QUEUED
redis> INCR user_id
QUEUED
redis> PING
QUEUED
redis> EXEC #执行
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
乐观锁实现watch
悲观锁:顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
# 第一步:
线程1:
[root@instance-6fuvnygy ~]# /yisa_oe/redis/redis-cli -c -p 6001
127.0.0.1:6001> set class 100
-> Redirected to slot [7755] located at 172.38.0.16:6379
OK
172.38.0.16:6379> watch class
OK
172.38.0.16:6379> multi
OK
172.38.0.16:6379> incr class
QUEUED
172.38.0.16:6379> # 待执行事务
# 第二步 抢先在客户端1执行事务前, 完成更新操作
# 模拟线程插队,线程2:
[root@instance-6fuvnygy ~]# /yisa_oe/redis/redis-cli -c -p 6001
127.0.0.1:6001> get class
-> Redirected to slot [7755] located at 172.38.0.16:6379
"100"
172.38.0.16:6379> set class 1
OK
# 第三步
# 回到线程1,执行事务:
172.38.0.16:6379> exec
(nil)
172.38.0.16:6379> unwatch
OK
172.38.0.16:6379> get class
"1"
持久化策略
1) 快照持久化rdb(redis database)
将某一时刻的所有数据写入硬盘。使用BGSAVE命令,随着内存使用量的增加,执行 BGSAVE 可能会导致系统长时间地停顿
优点:适合大批量数据恢复 ; 对数据完整性不高
缺点: 需要一定的时间间隔进行操作,如果redis意外宕机了,这个最后一次修改的数据就没有了;fork进程的时候,会占用一定的内容空间
rdb配置相关文件
# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000
#当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
stop-writes-on-bgsave-error yes
#使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间
rdbcompression yes
#rdb文件的名称
dbfilename dump.rdb
#数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /var/lib/redis
原理及流程图
原理
- 在进行 RDB 的时候,redis 的主线程是不会做 io 操作的,主线程会 fork 一个子线程来完成该操作;
- Redis 调用forks。同时拥有父进程和子进程。
- 子进程将数据集写入到一个临时 RDB 文件中。
- 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
触发持久化条件
-
满足配置文件中
save条件`save` 命令是同步命令,会占用Redis的主进程。若Redis数据非常多时,`save`命令执行速度会非常慢,阻塞所有客户端的请求。 -
执行清库命令
flushall -
关闭redis服务
`bgsave` 是异步进行,进行持久化的时候,redis 还可以将继续响应客户端请求 ;
bgsave和save对比
| 命令 | save | bgsave |
|---|---|---|
| IO类型 | 同步 | 异步 |
| 阻塞? | 是 | 是(阻塞发生在fock(),通常非常快) |
| 复杂度 | O(n) | O(n) |
| 优点 | 不会消耗额外的内存 | 不阻塞客户端命令 |
| 缺点 | 阻塞客户端命令 | 需要fock子进程,消耗内存 |
2) AOF 持久化(Append Only File)
只追加文件,在执行写命令时,将被执行的写命令复制到硬盘里面。使用 AOF 策略需要对硬盘进行大量写入,Redis 处理速度会受到硬盘性能的限制
优点:数据完整性好
缺点:相对于数据文件来说,aof远远大于rdb,修复速度比rdb慢;Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化
aof配置文件
#默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendonly no
#aof文件名
appendfilename "appendonly.aof"
#aof持久化策略的配置
#no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
#always表示每次写入都执行fsync,以保证数据同步到磁盘。
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
appendfsync everysec
3) rdb与aof对比
| RDB | AOF | |
|---|---|---|
| 启动优先级 | 低 | 高 |
| 体积 | 小 | 大 |
| 恢复速度 | 快 | 慢 |
| 数据安全性 | 丢数据 | 根据策略决定 |
发布订阅
发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
# 缺点
1. 如果一个客户端订阅了频道,但自己读取消息的速度却不够快的话,那么不断积压的消息会使redis输出缓冲区的体积变得越来越大,这可能使得redis本身的速度变慢,甚至直接崩溃。
2. 这和数据传输可靠性有关,如果在订阅方断线,那么他将会丢失所有在短线期间发布者发布的消息。
# 发布消息
172.38.0.16:6379> publish BIGDATA 2022
(integer) 1
# 订阅消息
172.38.0.16:6379> subscribe BIGDATA
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "BIGDATA"
3) (integer) 1
1) "message"
2) "BIGDATA"
3) "2022"
# 查看频道
172.38.0.16:6379> PUBSUB channels
1) "BIGDATA"
过期策略及内存淘汰机制
过期策略
Redis 的过期策略就是指当 Redis 中缓存的 Key 过期了,Redis 如何处理
- 定时过期:每个设置过期时间的 Key 创建定时器,到过期时间立即清除。内存友好,CPU 不友好
- 惰性过期:访问 Key 时判断是否过期,过期则清除。CPU 友好,内存不友好
- 定期过期:隔一定时间,expires 字典中扫描一定数量的 Key,清除其中已过期的 Key。内存和 CPU 资源达到最优的平衡效果
内存淘汰机制
172.38.0.16:6379> config get maxmemory-policy
maxmemory-policy
noeviction
- noeviction:新写入操作会报错
- allkeys-lru:移除最近最少使用的 key
- allkeys-random:随机移除某些 key
- volatile-lru:在设置了过期时间的键中,移除最近最少使用的 key
- volatile-random:在设置了过期时间的键中,随机移除某些 key
- volatile-ttl:在设置了过期时间的键中,有更早过期时间的 key 优先移除
为什么 Redis 是单线程的???
Redis 是基于内存的操作,CPU 不是 Redis 的瓶颈,Redis 瓶颈最有可能是内存或网络。而且单线程容易实现,避免了不必要的上下文切换和竞争条件,不存在多线程切换消耗 CPU
Redis是将所有的数据放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存存储数据情况下,单线程就是最佳的方案。
如何利用 CPU 多核心
在单机单实例下,如果操作都是 O(N)、O(log(N)) 复杂度,对 CPU 消耗不会太高。为了最大利用 CPU,单机可以部署多个实例
redis.conf 配置
1 #是否在后台执行,yes:后台运行;no:不是后台运行
2 daemonize yes
3
4 #是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,拒绝外部访问。
5 protected-mode yes
6
7 #redis的进程文件
8 pidfile /var/run/redis/redis-server.pid
9
10 #redis监听的端口号。
11 port 6379
12
16 #指定 redis 只接收来自于该 IP 地址的请求,如果不进行设置,那么将处理所有请求
18 bind 0.0.0.0
19
20 #配置unix socket来让redis支持监听本地连接。
21 # unixsocket /var/run/redis/redis.sock
25
26 # 此参数为设置客户端空闲超过timeout,服务端会断开连接,为0则服务端不会主动断开连接,不能小于0。
27 timeout 0
28
35 #指定了记录日志的文件。空字符串的话,日志会打印到标准输出设备。后台运行的redis标准输出是/dev/null。
36 logfile /var/log/redis/redis-server.log
46
47 #数据库的数量,默认使用的数据库是DB 0。可以通过SELECT命令选择一个db
48 databases 16
49
50 # redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
51 # 注释掉“save”这一行配置项就可以让保存数据库功能失效
52 # 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
53 # 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
54 # 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
55 save 900 1
56 save 300 10
57 save 60 10000
58
59 #当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
60 stop-writes-on-bgsave-error yes
61
62 #使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间
63 rdbcompression yes
67
68 #rdb文件的名称
69 dbfilename dump.rdb
70
71 #数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
72 dir /var/lib/redis
73
75 ############### 主从复制 ###############
76
77 #复制选项,slave复制对应的master。
78 # slaveof <masterip> <masterport>
79
80 #如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。masterauth就是用来配置master的密码,这样可以在连上master后进行认证。
81 # masterauth <master-password>
82
83 #当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续响应客户端的请求。2) 如果slave-serve-stale-data设置为no,除去INFO和SLAVOF命令之外的任何请求都会返回一个错误”SYNC with master in progress”。
84 slave-serve-stale-data yes
85
86 #作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)。
87 slave-read-only yes
88
89 #是否使用socket方式复制数据。目前redis复制提供两种方式,disk和socket。如果新的slave连上来或者重连的slave无法部分同步,就会执行全量同步,master会生成rdb文件。有2种方式:disk方式是master创建一个新的进程把rdb文件保存到磁盘,再把磁盘上的rdb文件传递给slave。socket是master创建一个新的进程,直接把rdb文件以socket的方式发给slave。disk方式的时候,当一个rdb保存的过程中,多个slave都能共享这个rdb文件。socket的方式就的一个个slave顺序复制。在磁盘速度缓慢,网速快的情况下推荐用socket方式。
90 repl-diskless-sync no
91
92 #diskless复制的延迟时间,防止设置为0。一旦复制开始,节点不会再接收新slave的复制请求直到下一个rdb传输。所以最好等待一段时间,等更多的slave连上来。
93 repl-diskless-sync-delay 5
94
95 #slave根据指定的时间间隔向服务器发送ping请求。时间间隔可以通过 repl_ping_slave_period 来设置,默认10秒。
96 # repl-ping-slave-period 10
97
98 #复制连接超时时间。master和slave都有超时时间的设置。master检测到slave上次发送的时间超过repl-timeout,即认为slave离线,清除该slave信息。slave检测到上次和master交互的时间超过repl-timeout,则认为master离线。需要注意的是repl-timeout需要设置一个比repl-ping-slave-period更大的值,不然会经常检测到超时。
99 # repl-timeout 60
100
101 #是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。默认是no,即使用tcp nodelay。如果master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络带宽。但是这也可能带来数据的延迟。默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择yes。
102 repl-disable-tcp-nodelay no
103
104 #复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。这样在slave离线的时候,不需要完全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状态。缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内存。没有slave的一段时间,内存会被释放出来,默认1m。
105 # repl-backlog-size 5mb
106
107 #master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为秒。
108 # repl-backlog-ttl 3600
109
110 #当master不可用,Sentinel会根据slave的优先级选举一个master。最低的优先级的slave,当选master。而配置成0,永远不会被选举。
111 slave-priority 100
112
113 #redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数小于N,mater就禁止写入。master最少得有多少个健康的slave存活才能执行写命令。这个配置虽然不能保证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入来避免数据丢失。设置为0是关闭该功能。
114 # min-slaves-to-write 3
115
116 #延迟小于min-slaves-max-lag秒的slave才认为是健康的slave。
117 # min-slaves-max-lag 10
118
119 # 设置1或另一个设置为0禁用这个特性。
120 # Setting one or the other to 0 disables the feature.
121 # By default min-slaves-to-write is set to 0 (feature disabled) and
122 # min-slaves-max-lag is set to 10.
123
124
125 ############### 安全相关 ###############
127 #requirepass配置可以让用户使用AUTH命令来认证密码,才能使用其他命令。这让redis可以使用在不受信任的网络中。为了保持向后的兼容性,可以注释该命令,因为大部分用户也不需要认证。使用requirepass的时候需要注意,因为redis太快了,每秒可以认证15w次密码,简单的密码很容易被攻破,所以最好使用一个更复杂的密码。注意只有密码没有用户名。
128 requirepass foobared # 密码设置
158
159 ############### APPEND ONLY 持久化方式 ###############
160
161 #默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
162 appendonly no
163
164 #aof文件名
165 appendfilename "appendonly.aof"
166
167 #aof持久化策略的配置
168 #no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
169 #always表示每次写入都执行fsync,以保证数据同步到磁盘。
170 #everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
171 appendfsync everysec
172
173 # 在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no。如果对延迟要求很高的应用,这个字段可以设置为yes,否则还是设置为no,这样对持久化特性来说这是更安全的选择。设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。Linux的默认fsync策略是30秒。可能丢失30秒数据。
174 no-appendfsync-on-rewrite no
175
176 #aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
177 auto-aof-rewrite-percentage 100
178 #设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
179 auto-aof-rewrite-min-size 64mb
180
181 #aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被载入内存。重启可能发生在redis所在的主机操作系统宕机后,尤其在ext4文件系统没有加上data=ordered选项(redis宕机或者异常终止不会造成尾部不完整现象。)出现这种现象,可以选择让redis退出,或者导入尽可能多的数据。如果选择的是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。如果是no,用户必须手动redis-check-aof修复AOF文件才可以。
182 aof-load-truncated yes
183
184
185 ############### LUA SCRIPTING ###############
186
187 # 如果达到最大时间限制(毫秒),redis会记个log,然后返回error。当一个脚本超过了最大时限。只有SCRIPT KILL和SHUTDOWN NOSAVE可以用。第一个可以杀没有调write命令的东西。要是已经调用了write,只能用第二个命令杀。
188 lua-time-limit 5000
189
190
191 ############### 集群相关 ###############
192
193 #集群开关,默认是不开启集群模式。
194 # cluster-enabled yes
195
196 #集群配置文件的名称,每个节点都有一个集群相关的配置文件,持久化保存集群的信息。这个文件并不需要手动配置,这个配置文件有Redis生成并更新,每个Redis集群节点需要一个单独的配置文件,请确保与实例运行的系统中配置文件名称不冲突
197 # cluster-config-file nodes-6379.conf
198
199 #节点互连超时的阀值。集群节点超时毫秒数
200 # cluster-node-timeout 15000
201
202 #在进行故障转移的时候,全部slave都会请求申请为master,但是有些slave可能与master断开连接一段时间了,导致数据过于陈旧,这样的slave不应该被提升为master。该参数就是用来判断slave节点与master断线的时间是否过长。判断方法是:
203 #比较slave断开连接的时间和(node-timeout * slave-validity-factor) + repl-ping-slave-period
204 #如果节点超时时间为三十秒, 并且slave-validity-factor为10,假设默认的repl-ping-slave-period是10秒,即如果超过310秒slave将不会尝试进行故障转移
205 # cluster-slave-validity-factor 10
206
207 #master的slave数量大于该值,slave才能迁移到其他孤立master上,如这个参数若被设为2,那么只有当一个主节点拥有2 个可工作的从节点时,它的一个从节点会尝试迁移。
208 # cluster-migration-barrier 1
209
210 #默认情况下,集群全部的slot有节点负责,集群状态才为ok,才能提供服务。设置为no,可以在slot没有全部分配的时候提供服务。不建议打开该配置,这样会造成分区的时候,小分区的master一直在接受写请求,而造成很长时间数据不一致。
211 # cluster-require-full-coverage yes
212
213
214 ############### SLOW LOG 慢查询日志 ###############
215
216 ###slog log是用来记录redis运行中执行比较慢的命令耗时。当命令的执行超过了指定时间,就记录在slow log中,slog log保存在内存中,所以没有IO操作。
217 #执行时间比slowlog-log-slower-than大的请求记录到slowlog里面,单位是微秒,所以1000000就是1秒。注意,负数时间会禁用慢查询日志,而0则会强制记录所有命令。
218 slowlog-log-slower-than 10000
219
220 #慢查询日志长度。当一个新的命令被写进日志的时候,最老的那个记录会被删掉。这个长度没有限制。只要有足够的内存就行。你可以通过 SLOWLOG RESET 来释放内存。
221 slowlog-max-len 128
慢查询
配置文件
#执行时间比slowlog-log-slower-than大的请求记录到slowlog里面,单位是微秒,所以1000000就是1秒。注意,负数时间会禁用慢查询日志,而0则会强制记录所有命令。
slowlog-log-slower-than 10000
#慢查询日志长度。当一个新的命令被写进日志的时候,最老的那个记录会被删掉。这个长度没有限制。只要有足够的内存就行。你可以通过 SLOWLOG RESET 来释放内存。
slowlog-max-len 128
查看慢日志命令
SLOWLOG LEN # 查看日志数量
SLOWLOG RESET #重置日志
缓存穿透与雪崩
缓存穿透(查不到)
概念
在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)缓存都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,就有可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。
解决方案
布隆过滤器:
对所有可能查询的参数以Hash的形式存储,以便快速确定是否存在这个值,在控制层先进行拦截校验,校验不通过直接打回,减轻了存储系统的压力。
缓存空对象:
一次请求若在缓存和数据库中都没找到,就在缓存中方一个空对象用于处理后续这个请求。
这样做有一个缺陷:存储空对象也需要空间,大量的空对象会耗费一定的空间,存储效率并不高。解决这个缺陷的方式就是设置较短过期时间
即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
缓存击穿(量太大,缓存过期)
概念
相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。
比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。
解决方案
设置热点数据永不过期
这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。
加互斥锁(分布式锁)
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。这样对锁的要求就十分高。
缓存雪崩
概念
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
解决方案
redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群
限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
redis集群的3中方式
主从复制
主从工作模式
- 从服务器连接主服务器,发送SYNC命令;
- 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
- 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
- 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
- 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
- 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;(从服- 务器初始化完成)
- 主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令(从服务器初始化完成后的操作)
优缺点
| 优点 | 缺点 |
|---|---|
| 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离 Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。 | Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。 |
| Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。 | 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。 |
| Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据 | Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂 |
哨兵模式
哨兵的工作方式
- 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令。
- 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过
down-after-milliseconds选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN) - 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态
- 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)
- 在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令。
- 当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
- 若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
优缺点
| 优点 | |
|---|---|
| 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。 | Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。 |
| 主从可以自动切换,系统更健壮,可用性更高。 | 每台redis服务器都存储相同的数据,很浪费内存 |
哨兵模式配置文件
# Example sentinel.conf
# 1、哨兵sentinel 实例运行的端口 默认26379
port 26379
# 2、 哨兵 sentinel 的工作目录
dir "/usr/local/bin"
# 3、哨兵sentinel监控的redis主节点 host port
# - master-name 可以自己对 主节点 明明
# - quorum 配置多少个sentinel 哨兵认为master 主节点失联,那么这个时候就客观的认为失联了
# sentinel monitor master-name host port quorum
sentinel monitor myredis 127.0.0.1 6379 1
# 4、在Redis实例中开启了密码,这时,所有连接Redis的客户端都需要密码
# - 设置了哨兵sentinel 连接上主从的密码,注意必须设置一样的验证码
# sentinel auth-pass master-name password
sentinel auth-pass myredis yisa123456q
# 5、指定多少毫秒后 主节点没有回答哨兵sentinel 此时 哨兵主观上认为主节点离线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds myredis 3000
# 6、这个配置指定了在发生failover 主备切换时最多可以由多少个slave同时对新的master进行同步
- 这个数字越小,完成failover 所需的时间越长
- 这个数字越大,就意味着越多的slave 因为repkication 而不可用
- 可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态
# sentinel parallel-syncs <master-name> <numreplicas>
sentinel parallel-syncs mymaster 1
# 7、故障转移的时间 failover-timeout 可以用一下这些方面
同一个sentinel 对同一个master 两次failover 之间的间隔时间
当想要取消一个正在进行的fai1over所需要的时间,直到slave 被纠正为向正确的master那里同步数据时
当进行fai1over时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,s1aves依然会被正确配置为指向 master,但是就不按para11e1-syncs所配置的规则来了
# sentinel failover-timeout <master-name> <milliseconds> 默认三分钟
sentinel failover-timeout mymaster 180000
# 8、配置当某一个事件发生时需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时,发送
邮件通知相关人员
- 对于脚本的运行结果有以下规则:
1.若脚本执行后返回1,那么该脚本稍后会重新执行,重复次数默认为10
2.若脚本执行后返回2,或者是比2更高的返回值,脚本将不会执行
3.若脚本在执行过程中由于收到系统中断信号被终止了,则同返回值1的时候的相同
4.一个脚本执行的最大时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,重新执行
- 通知型脚本:当sentine1有任何警告级别的事件发生时(比如说re dis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentine1.conf配置文件中配置了这个脚本路路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则 sentine1无法正常启动成功。
# sentinel notification-script <master-name> <script-path>
sentinel notification-script mymaster /var/redis/notify.sh
# 9、客户端重新配置主节点参数脚本
- 当一个master 发生改变时,这个脚本就会被调用,通知相关的客户端关于 master 地址已经发生改变
- 一下参数将会在调用脚本的时候传给脚本
1. <master-name> <role> <state> <from-ip><from-port><to-ip><to-port>
2. 目前<state>总是"failover”
3. <ro1e>是"leader"或者"observer”中的一个。
4. 参数from-ip,from-port,to-ip,to-port是用来和旧的master和新的master(即旧的s1ave)通信的
5. 这个脚本应该是通用的,能被多次调用,不是针对性的
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
redis集群模式
工作模式
- Redis的集群模式,实现了数据的分布式存储,对数据进行分片,将不同的数据存储在不同的master节点上面,从而解决了海量数据的存储问题。
- Redis集群采用去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以连接任意一个节点进行操作,就像操作单一Redis实例一样,不需要任何代理中间件,当客户端操作的key没有分配到该node上时,Redis会返回转向指令,指向正确的node。
- Redis也内置了高可用机制,支持N个master节点,每个master节点都可以挂载多个slave节点,当master节点挂掉时,集群会提升它的某个slave节点作为新的master节点。
- 在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
redis实战部署
[root@instance-6fuvnygy ~]# netstat -nltp | grep redis
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN 4637/redis-server 0
tcp 0 0 0.0.0.0:6380 0.0.0.0:* LISTEN 4642/redis-server 0
tcp 0 0 0.0.0.0:6381 0.0.0.0:* LISTEN 4647/redis-server 0
1.主从搭建
redis服务默认都是主节点,配置主从只需要在从服务器上面配置即可,使用slaveof命令配置,默认从服务器只能读不能写,写操作在主节点上,如果主节点有配置密码,从节点配置文件均需配置主节点的密码 masterauth
6379 为主 6380 6381为从
- 配置从6380、6381服务器
# 6380配置主节点
127.0.0.1:6380> slaveof 127.0.0.1 6379
OK
# 查看6380服务信息
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:0
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:0ea199ea7e6c82e8687a461d48fb042536715993
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:0
127.0.0.1:6380> set name 1111
(error) READONLY You can't write against a read only replica.
- 查看主节点6379相关信息
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=448,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=448,lag=1
master_replid:0ea199ea7e6c82e8687a461d48fb042536715993
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:448
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:448
# 6379端口设置内容
127.0.0.1:6379> set name 1234
OK
# 6380端口获取内容
127.0.0.1:6380> get name
"1234"
2.哨兵集群搭建
- 设置配置文件,sentinel.conf文件
# 配置哨兵端口
port 26379
# 配置主节点服务
sentinel monitor myredis 127.0.0.1 6379 1
# 配置主节点连接的密码
sentinel auth-pass myredis yisa123456q
# 哨兵判断主节点主观下线的时长 毫秒
sentinel down-after-milliseconds myredis 3000
- 启动哨兵
[root@instance-6fuvnygy redis]# ./redis-sentinel sentinel.conf
34200:X 26 Jun 2022 16:19:52.012 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
34200:X 26 Jun 2022 16:19:52.012 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=34200, just started
34200:X 26 Jun 2022 16:19:52.012 # Configuration loaded
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 5.0.3 (00000000/0) 64 bit
.-`` .-```. ```/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 34200
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
34200:X 26 Jun 2022 16:19:52.014 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
34200:X 26 Jun 2022 16:19:52.031 # Sentinel ID is 26919c134f310ec7d3f8eedb2be88d4f7168484a
34200:X 26 Jun 2022 16:19:52.031 # +monitor master myredis 127.0.0.1 6379 quorum 1
34200:X 26 Jun 2022 16:19:52.032 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:19:52.033 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
- shundown 主节点后
# redis-sentinel相关信息
34200:X 26 Jun 2022 16:21:57.489 # +sdown master myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:57.489 # +odown master myredis 127.0.0.1 6379 #quorum 1/1
34200:X 26 Jun 2022 16:21:57.489 # +new-epoch 1
34200:X 26 Jun 2022 16:21:57.489 # +try-failover master myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:57.491 # +vote-for-leader 26919c134f310ec7d3f8eedb2be88d4f7168484a 1
34200:X 26 Jun 2022 16:21:57.491 # +elected-leader master myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:57.491 # +failover-state-select-slave master myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:57.574 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:57.574 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:57.646 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:58.365 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:58.365 # +failover-state-reconf-slaves master myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:58.464 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:59.391 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:59.391 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:59.453 # +failover-end master myredis 127.0.0.1 6379
34200:X 26 Jun 2022 16:21:59.453 # +switch-master myredis 127.0.0.1 6379 127.0.0.1 6380
34200:X 26 Jun 2022 16:21:59.453 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ myredis 127.0.0.1 6380
34200:X 26 Jun 2022 16:21:59.453 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6380
34200:X 26 Jun 2022 16:22:29.481 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6380
- 启动6379 并查看6380 服务信息
# 6379 服务器相关信息
[root@instance-6fuvnygy redis]# /yisa_oe/redis/redis-cli
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6380
master_link_status:up
master_last_io_seconds_ago:0
master_sync_in_progress:0
slave_repl_offset:30907
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:087646d5796b1f02769b3f141c6923fa5602b30a
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:30907
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:27793
repl_backlog_histlen:3115
# redis-sentinel相关信息
34200:X 26 Jun 2022 16:27:05.575 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ myredis 127.0.0.1 6380
# 6380节点相关信息
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=38599,lag=1
slave1:ip=127.0.0.1,port=6379,state=online,offset=38599,lag=1
master_replid:087646d5796b1f02769b3f141c6923fa5602b30a
master_replid2:0ea199ea7e6c82e8687a461d48fb042536715993
master_repl_offset:38731
second_repl_offset:7272
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:351
repl_backlog_histlen:38381
3.集群搭建实战
- 配置文件
port 7000 //端口7000,7002,7003
bind 本机ip //改为其他节点机器可访问的ip 可以使用ifconfig查看一下
daemonize yes //redis后台运行
appendonly yes //aof日志开启 有需要就开启,它会每次写操作都记录一条日志
pidfile /var/run/redis_7000.pid //pidfile文件对应7000,7001,7002
cluster-enabled yes //开启集群
cluster-config-file nodes_7000.conf //集群的配置 配置文件首次启动自动生成 7000,7001,7002
cluster-node-timeout 15000 //请求超时 默认15秒,可自行修改
- 启动redis服务
redis-server redis_cluster/7000/redis.conf
redis-server redis_cluster/7001/redis.conf
redis-server redis_cluster/7002/redis.conf
redis-server redis_cluster/7003/redis.conf
redis-server redis_cluster/7004/redis.conf
redis-server redis_cluster/7005/redis.conf
- 启动
redis-cli --cluster create --cluster-replicas 1 121.37.161.244:7000 121.37.161.244:7001 121.37.161.244:7002 121.37.161.244:7003 121.37.161.244:7004 121.37.161.244:7005
# --cluster-replicas 1 一主带一从
- 连接redis集群
redis-cli -c -p 6379
-c # 表示连接集群
- 集群状态,集群搭建成功
172.38.0.16:6379> cluster nodes
5aae196eb52afd886f2dbec 172.38.0.16:6379@16379 myself,master - 0 1655654115000 7 connected 5461-10922
b4189d59bd303b9b58ec6e8 172.38.0.15:6379@16379 slave 3c7978360203b50d7e6269d61 0 1655654115000 1 connected
60290da62f04de060f1c3ae 172.38.0.14:6379@16379 slave 026a47644b7997b560426fca6 0 1655654115518 3 connected
3c7978360203b50d7e6269d 172.38.0.11:6379@16379 master - 0 1655654115518 1 connected 0-5460
026a47644b7997b560426fc 172.38.0.13:6379@16379 master - 0 1655654116521 3 connected 10923-16383
306fd6cbe116159e02e1b73 172.38.0.12:6379@16379 slave 5aae196eb52afd886f2dbec241 0 1655654116000 7 connected
Redis 与 Memcache 区别
| 对比项 | Redis | Memcache |
|---|---|---|
| 数据结构 | 丰富数据类型 | 只支持简单 KV 数据类型 |
| 数据一致性 | 事务 | cas |
| 持久性 | 快照/AOF | 不支持 |
| 网络IO | 单线程 IO 复用 | 多线程、非阻塞 IO 复用 |
| 内存管理机制 | 现场申请内存 | 预分配内存 |
\