Redis学习笔记
把以前学习Redis做的笔记上传一下
使用redis
一、连接redis
1. 开启redis服务
redis-server
# 指定配置文件
redis-server redis.conf
2. 启动redis客户端
redis-cli
# 避免中文乱码
redis-cli --raw
3. 检测是否启动redis服务
ping
命令
[root@TEST-1 bin]# redis-cli
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth Charramma123#
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>
4. 登录
auth
命令,验证密码是否正确
127.0.0.1:6379> auth username password
不加用户名就是登录root用户
5. 远程连接redis
redis-cli -h host -p port -a password
6. 切换到指定的数据库
每个数据库对外都是以一个从0开始的递增数字命名,不支持自定义数据库名。redis默认支持16个数据库,可以通过配置参数databases来修改这一数字。客户端与redis建立连接后悔自动选择0号数据库
flushall命令会清空所有数据库。
redis的数据库更像是一种命名空间。
127.0.0.1:6379> SELECT <index>
7. 关闭连接
127.0.0.1:6379> QUIT
8. 查看及设置最大连接数
查看最大连接数
127.0.0.1:6379> CONFIG GET maxclients
1) "maxclients"
2) "10000"
启动服务时设置最大连接数
redis-server --maxclients 100000
命令 | 描述 |
---|---|
CLIENT LIST | 返回连接到redis服务的客户端列表 |
CLIENT SETNAME | 设置当前连接的名称 |
CLIENT GETNAME | 获取通过CLIENT SETNAME命令设置的服务名称 |
CLIENT PAUSE | 挂起客户端连接,指定挂起的时间以毫秒计 |
CLIENT KILL | 关闭客户端连接 |
二、redis数据类型
redis支持5种数据类型
string(字符串)、list(列表)、hash(哈希)、list(列表)、set(集合)及zset(有序集合)
1. string(字符串)
127.0.0.1:6379> SET name "Tom"OK127.0.0.1:6379> GET name"Tom"
相关命令
命令 | 描述 |
---|---|
SET key value | 指定key的值 |
GET key | 获取key的值 |
GETSET key value | 设定新值返回旧值 |
MGET key1 key2... | 获取多个key的值 |
MSET key1 value1 key2 value2... | 同时设置多个key的值 |
STRLEN key | 返回字符串长度 |
APPEND key value | 追加新值到旧值的末尾 |
DEL key | 删除key |
2. Hash(哈希)
127.0.0.1:6379> HMSET name field1 "Tom" field2 "Jerry"OK127.0.0.1:6379> HGET name field1"Tom"127.0.0.1:6379> HGET name field2"Jerry"
相关命令
命令 | 描述 |
---|---|
HDEL key field1 [field2] | 删除一个或多个哈希表字段 |
HEXISTS key field | 查看哈希表 key 中,指定的字段是否存在。 |
HMSET key field1 value field2 value | 设置值 |
HGET key field | 获取指定字段的值 |
HGETALL key | 获取在哈希表中指定 key 的所有字段和值 |
HKEYS key | 获取所有哈希表中的字段 |
HLEN key | 获取哈希表中字段的数量 |
HMGET key field1 [field2] | 获取所有给定字段的值 |
HSET key field value | 将哈希表 key 中的字段 field 的值设为 value 。 |
DEL key | 清空hash表 |
3. List(列表)
127.0.0.1:6379> lpush name "Tom"
(integer) 1
127.0.0.1:6379> lpush name "Jerry"
(integer) 2
127.0.0.1:6379> lrange name 0 3
1) "Jerry"
2) "Tom"
获取列表元素序号由0开始结束序号大于实际长度也没关系
相关命令
命令 | 描述 |
---|---|
lpush key value | 将一个或多个值插入列表头部 |
lrange key start end | 获取列表指定范围内的元素 |
lrem key count value | 移除列表元素 |
lset key index value | 通过索引设置列表元素的值 |
llen key | 获取列表长度 |
lpop key | 移出并获取列表第一个插入的元素 |
rpop key | 移出并获取列表最后一个插入的元素 |
lrem key count value
count > 0 从表头开始向表尾搜索,移除数量为count的与value相等的值
count = 0 移除表中所有与value相等的值
count < 0 从表尾向表头搜索,移除数量为count的与value相等的值
4. Set(集合)
127.0.0.1:6379> sadd name "Tom"(integer) 1127.0.0.1:6379> sadd name "Jerry"(integer) 1127.0.0.1:6379> sadd name "Jerry"(integer) 0127.0.0.1:6379> smembers name1) "Jerry"2) "Tom"
集合元素特点:
- 无序
- 唯一
相关命令
命令 | 描述 |
---|---|
sadd key member | 添加一个或多个成员 |
scard key | 获取集合的成员数 |
sdiff key1 key2 | 获取第一个集合和其他集合之间的差异 |
sinter key1 key2 | 返回给定集合之间的交集 |
sismember key member | 判断member元素是否是集合key的成员 |
smembers key | 返回集合中的所有成员 |
srandmember key [count] | 随机返回集合中一个或多个成员 |
sunion key1 key2 | 返回给定集合的并集 |
smove key1 key2 member | 将member元素从集合1移动到集合2中 |
5. Zset(有序集合)
zadd key score member
根据score来排序
127.0.0.1:6379> zadd name 0 "Tom"(integer) 1127.0.0.1:6379> zadd name 1 "Jerry"(integer) 1127.0.0.1:6379> zadd name 3 "Harry"(integer) 1127.0.0.1:6379> zadd name 2 "Poly"(integer) 1127.0.0.1:6379> zrangebyscore name 0 1001) "Tom"2) "Jerry"3) "Poly"4) "Harry"127.0.0.1:6379>
如果score一样,按ASCII值排序
127.0.0.1:6379> zadd alpha 0 "A"(integer) 1127.0.0.1:6379> zadd alpha 0 "C"(integer) 1127.0.0.1:6379> zadd alpha 0 "B"(integer) 1127.0.0.1:6379> zadd alpha 0 "D"(integer) 1127.0.0.1:6379> zadd alpha 0 "G"(integer) 1127.0.0.1:6379> zadd alpha 0 "E"(integer) 1127.0.0.1:6379> zadd alpha 0 "F"(integer) 1127.0.0.1:6379> zrangebyscore alpha 0 101) "A"2) "B"3) "C"4) "D"5) "E"6) "F"7) "G"
相关命令
命令 | 描述 |
---|---|
zadd key score member | 向集合中添加一个或多个成员,或更新成员的score |
zcard key | 获取有序集合的成员数 |
zcount key min max | 计算在有序集合中指定score区间的成员数 |
zlexcount key start end | 在有序集合中计算指定字典区间内成员数量 |
zrange key min max | 通过索引区间返回有序集合指定区间内的成员 |
zrangebyscore key min max | 通过分数返回有序集合指定区间内的成员 |
zrank key member | 返回有序集合中指定成员的索引 |
zrem key member | 移除有序集合中的一个或多个成员 |
zscore key member | 返回有序集合中成员的分数值 |
三、redis常用命令
1. 汇总
官方命令文档:https://redis.io/commands
命令文档中文:http://www.redis.cn/commands.html
############ 连接操作命令
quit 关闭连接
auth 简单密码连接
help cmd 查看cmd版主
############ 持久化
save 将数据同步保存到磁盘
bgsave 将数据异步保存到磁盘
lastsave 返回上次成功将数据保存到磁盘的Unix时戳
shutdown 将数据同步保存到磁盘,然后关闭服务
############ 远程服务控制
info 提供服务器的信息和统计
minitor 实时转储收到的请求
slaveof 改变复制策略设置
config 在运行时配置redis服务器
############ 对value操作的命令
exists(key) 确认一个key是否存在
del(key) 删除一个key
type(key) 返回值的类型
keys(pattern) 返回满足给定pattern的所有key
randomkey 随机返回key空间的一个
keyrename(oldname, newname) 重命名key
dbsize 返回当前数据库中key的数目
expire 设定一个key的活动时间(s)
ttl 获得一个key的活动时间
select(index) 按索引查询
move(key, dbindex) 移动当前数据库中的key到dbindex数据库
flushdb 删除当前选择数据库中的所有key
flushall 删除所有数据库中的所有key
2. 操作key相关命令
### 删除key
DEL <key_name>
### 序列化给定key,返回序列化后的值
DUMP <key_name>
### 检查key是否存在
EXISTS <key_name>
### 修改key的名称
RENAME <old_key_name> <new_key_name>
### 移动key到给定的数据库中
MOVE <key_name> <db_name>
### 查看有哪些key# 语法:
# 查看所有以a开头的key
keys patternkeys a*
# 查看所有的key
keys *
### 查看key的类型
TYPE <key_name>
3. 过期时间 expire
通常Redis keys创建时没有设置相关过期时间。他们会一直存在,除非使用显示的命令移除,例如,使用DEL命令。
redis有四个命令可以用于设置键的生存时间:
命令 | 说明 |
---|---|
EXPIRE | 用于将键key的生存时间设置为ttl秒 |
PEXPIRE | 用于将键key的生存时间设置为ttl毫秒 |
EXPIREAT |
用于将键key的过期时间设置为timestamp所指定的秒数时间戳 |
PEXPIREAT |
用于将键key的过期时间设置为timestamp所指定的毫秒数时间戳 |
PERSIST | 移除key的过期时间 |
实例:设置name的过期时间为20秒,20秒后,redis会吧name删掉
127.0.0.1:6280> set name 'Tom'OK127.0.0.1:6280> expire name 20(integer) 1127.0.0.1:6280> get name"Tom"127.0.0.1:6280> get name(nil)127.0.0.1:6280>
4. 数据备份与恢复
使用SAVE
命令进行数据库备份,该命令将在 redis 安装目录中创建dump.rdb文件。
127.0.0.1:6280> SAVE
# 查看安装目录
127.0.0.1:6280> config get dir
1) "dir"
2) "/usr/local/redis-6.2.4/data"
恢复数据:重启redis服务,redis会自动从安装目录中读取dump.rdb
四、管道
一次执行多个命令,节省往返时间
redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务
一个redis请求过程:
- 客户端向服务端发送请求,监听socket返回
- 服务端处理命令,返回给客户端
依次向服务端发送多个请求,如果前一个请求在服务器未得到响应,后一个请求会等待
Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。
$(echo -en "PING\r\n SET runoobkey redis\r\nGET runoobkey\r\nINCR visitor\r\nINCR visitor\r\nINCR visitor\r\n"; sleep 10) | nc localhost 6379+PONG+OKredis:1:2:3
选择管道还是脚本:
如果是组织大量的、无依赖关系的命令,可以选择管道,当然也可以选择脚本。
如果命令之间有依赖关系,比如后续的命令需要处理先前命令的返回值,只能选择脚本。
五、redis发布订阅(PUB/SUB)
- 是一种消息通信模式:发送者(PUB)发送消息,订阅者(SUB)接收消息
- redis客户端可以订阅任意数量的频道
相关命令
命令 | 描述 |
---|---|
PSUBSCRIBE patern | 订阅一个或多个符合给定模式的频道 |
PUBSUB subcommand [argument] | 查看订阅与发布系统状态 |
PUBLISH channel message | 将信息发送到指定的频道 |
PUNSUBSCRIBE [pattern] | 退订所有给定模式的频道 |
SUBSCRIBE channel | 订阅给定的一个或多个频道信息 |
UNSUBSCRIBE channel | 退订给定的频道 |
示例:
################## 订阅频道msg
127.0.0.1:6280> SUBSCRIBE msg
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "msg"
3) (integer) 1
###################### 发送消息
127.0.0.1:6280> PUBLISH msg "lalala"
(integer) 1
##################### 订阅者收到消息
127.0.0.1:6280> SUBSCRIBE msg
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "msg"
3) (integer) 1
1) "message"
2) "msg"
3) "lalala"
Q: pub/sub有什么缺点?
A: 在消费者(sub)下线的情况下,生产的消息会丢失,需要使用专业的消息队列如rocketMQ
六、redis脚本
redis从2.6开始支持Lua脚本,redis内置了Lua环境
1. eval命令
eval命令使用内置Lua解释器执行脚本
语法:
127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]
- eval:执行
Lua
脚本的命令。 - lua-script:
Lua
脚本内容。 - numkeys:表示的是
Lua
脚本中需要用到多少个key
,如果没用到则写0
。 - key [key ...]:将
key
作为参数按顺序传递到Lua
脚本,numkeys
是0
时则可省略。 - arg:
Lua
脚本中用到的参数,如果没有可省略。
示例:
# 一个不带参数的Lua脚本命令127.0.0.1:6280> eval "return 'hello Redis'" 0"hello Redis"# 带参数的Lua脚本命令127.0.0.1:6280> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second1) "key1"2) "key2"3) "first"4) "second"
2. 执行redis命令
使用redis.call()
或redis.pcall()
实例:
127.0.0.1:6280> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 name TomOK127.0.0.1:6280> get name"Tom"
3. Lua脚本摘要
每次调用完Lua脚本的时候,redis会为每个Lua脚本生成一个摘要。也可以使用script load
命令手动生成摘要。
script load command
生成脚本摘要
evalsha 摘要值
使用脚本摘要
script exist 摘要值
判断摘要是否存在,0 不存在 1 存在
script flush
清除所有的脚本缓存
# 给当前脚本生成摘要并返回127.0.0.1:6280> script load "return redis.call('set',KEYS[1],ARGV[1])""c686f316aaf1eb01d5a4de1b0b63cd233010e63d"# 使用脚本摘要执行脚本127.0.0.1:6280> evalsha "c686f316aaf1eb01d5a4de1b0b63cd233010e63d" 1 address ChinaOK127.0.0.1:6280> get address"China"# 清除所有脚本缓存 script flush127.0.0.1:6280> script exists "c686f316aaf1eb01d5a4de1b0b63cd233010e63d"1) (integer) 1127.0.0.1:6280> script flushOK127.0.0.1:6280> script exists "c686f316aaf1eb01d5a4de1b0b63cd233010e63d"1) (integer) 0
4. Lua脚本文件
如果要使用很长的脚本,建议写在文件中。
cat > test.lua << EOF
redis.call('set',KEYS[1],ARGV[1])
return redis.call('get',KEYS[1])
EOF
# key和arg参数之间要用逗号隔开
redis-cli --eval test.lua 1 age , 18
脚本超时时间
为了避免redis执行脚本卡住或陷入死循环,可以在配置文件中指定
lua-time-limit
控制Lua脚本执行的超时时间,单位是毫秒,到达超时时间之后Lua会自动中断脚本。
强制退出脚本
script kill
强制Lua脚本中断执行,但是要求该脚本没有成功执行过命令
shtudown nosave
强制退出Lua脚本
5. Lua脚本调试
从版本3.2开始,Redis包含一个完整的Lua调试器(Lua debugger, 简称LDB)
创建一个debug会话
-
前提:本地有一个Lua脚本,假设叫做test.lua
local foo = redis.call('ping') return foo
-
进入debug模式
# --ldb参数进入debug模式 # --eval参数指定需要debug的Lua脚本 redis-cli --ldb --eval test.lua
实例:
执行
redis-cli--ldb --eval test.lua
后开启了一个debugging会话,代码停在了第一行位置[root@Charra-Ali ~]# redis-cli--ldb --eval test.lua Lua debugging session started, please use:quit -- End the session.restart -- Restart the script in debug mode again.help -- Show Lua script debugging commands.* Stopped at 1, stop reason = step over-> 1 local foo = redis.call('ping')lua debugger>
使用step命令执行下一行
lua debugger> step<redis> ping # 表示脚本执行内容<reply> "+PONG" # 表示脚本执行返回结果* Stopped at 2, stop reason = step over-> 2 return foo
再次执行
step
,此时脚本代码已经执行完成,debugging会话结束,输出脚本返回结果lua debugger> stepPONG(Lua debugging session ended -- dataset changes rolled back)
断点:
添加断点:break <line>
。可以添加多个行号,用空格隔开。
单独一个break
命令可以查看断点
添加断点后使用continue命令让脚本执行到断点处
lua debugger> break 5lua debugger> break1 breakpoints set:->#5 while count > 0 dolua debugger> continue
删除断点:break -<line>
删除所有断点:break 0
lua debugger> break -5Breakpoint removed.lua debugger> break 0All breakoints removed.
动态添加断点:在脚本中调用redis.breakpoint()
函数设置断点,执行到该函数时,会在该函数下一行模拟一个断点
向控制台输出:redis.debug()
,redis.debug()可以接收多个参数,参数之间用逗号隔开
local a = {1,2,3}local b = falseredis.debug(a,b)
说明:
-
当某个客户端向服务端发起
debugging
会话的时候,并不会阻塞服务端,即服务端仍能正常的处理请求,而且也能同时处理多个debugging
会话--ldb-sync-mode
参数指定LDB为同步模式,在该工作模式下调整Lua脚本会阻塞服务器,debug
的过程中服务端不会处理其他请求,并且不会对数据做回滚操作 -
当
debugging
会话结束的时候,Lua
脚本中对redis
数据的所有修改都会回滚 -
使用
restart
重新开启debug
会话时,每次的执行效果都相同(回滚了) -
LDB
在设计命令的时候,每个不同的命令的首字母都不一样,因此可以使用首字母缩写的方式代替命令全拼,如print
命令,可以使用其首字母p
代替
七、redis事务
Redis
只提供了简单的事务功能。本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。命令的执行过程是是原子顺序执行的,但是不能保证原子性。
原子性:要么全部执行成功,要么全部执行失败
事务从开始到结束的三个阶段:
- 事务开始
- 命令入队
- 事务执行
相关命令:
命令 | 说明 |
---|---|
MULIT | 开启事务,redis将后续的命令逐个放入队列中,然后使用EXEC命令原子化执行 |
EXEC | 提交事务 |
DISCARD | 取消事务 |
WATCH | 监视一个或多个key |
UNWATCH | 取消WATCH对所有key的监视 |
-
当 Redis 开启一个事务后,添加对应的命令出现了语法错误时,会导致事务提交失败。
-
当 Redis 开启一个事务后,添加对应的命令到 Redis 并没有报编译错误。也就是说事务里面的命令 Redis 都可以执行,在运行时检测到了类型错误,最终导致事务提交失败,此时事务并没有回滚,而是跳过错误命令继续执行。
-
Redis 使用
WATCH
命令来决定事务是继续执行还是回滚,确保事务中的key没
有被其他客户端修改过,才执行事务,否则不执行(类似乐观锁)。当使用EXEC
执行事务时,首先会比对WATCH
所监控的键值对,如果没发生改变,它会执行事务队列中的命令,提交事务;如果发生变化,将不会执行事务中的任何命令,同时事务回滚。当然无论是否回滚,Redis 都会取消执行事务前的WATCH
命令。127.0.0.1:6379> set k1 v1OK127.0.0.1:6379> set k2 v2OK127.0.0.1:6379> WATCH k1OK127.0.0.1:6379> set k1 11OK127.0.0.1:6379> MULTIOK127.0.0.1:6379> set k1 12QUEUED127.0.0.1:6379> set k2 22QUEUED127.0.0.1:6379> EXEC(nil)127.0.0.1:6379> get k1"11"127.0.0.1:6379> get k2"v2"
八、redis持久化
https://juejin.cn/post/6844903648976175118
1. 什么是redis持久化
redis数据都存储在内存当中,在处理客户端请求时,所有操作都在内存当中进行。
内存中的数据不能持久保存,因此选择将数据从内存中保存到磁盘中,使数据可以持久化保存。
redis提供了两种数据持久化方式:RDB
和AOF
我发现退出redis进程,redis会自动保存数据到安装目录的dump.rdb中
2. RDB
通过在客户端数据save、bgsave命令或者达到配置文件自动保存快照条件时,将redis在内存中的数据生成快照保存在名字为dump.rdb的二进制文件中。
save
save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求。
bgsave
redis服务端派生一个子进程,子进程将数据写入到一个临时RDB文件中,当子进程完成对新RDB文件的写入时,redis用新RDB文件替换原来的RDB文件,并删除旧的RDB文件
RDB配置
### 配置文件自动保存条件save 900 1 # 900 秒内有至少有 1 个键被改动save 300 10 # 300 秒内有至少有 10 个键被改动save 60 10000 # 60 秒内有至少有 1000 个键被改动### 设置在保存快照出错时,是否停止redis命令的写入stop-writes-on-bgsave-error yes### 是否在导出.rdb数据库文件的时候采用LZF压缩rdbcompression yes### 是否开启CRC64校验rdbchecksum yes### 导出数据库的文件名称dbfilename dump.rdb### 导出的数据库所在的目录dir ./
RDB的优点
- 保存某个时间点的数据,适用于数据的备份
- 方便传送到拎一个远端数据中心,适用于灾备
- RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能.
- 在恢复大的数据集时,与AOF相比更快一些
RDB的缺点
- Redis意外宕机 时,会丢失部分数据
- 当Redis数据量比较大时,fork的过程是非常耗时的,fork子进程时是会阻塞的,在这期间Redis 是不能响应客户端的请求的。
3. AOF
AOF
持久化方式会记录客户端对服务器的每一次写操作命令,并将这些写操作以Redis
协议追加保存到以后缀为aof
文件末尾,在Redis服务器重启时,会加载并运行aof
文件的命令,以达到恢复数据的目的。
示意图:
- 所有写命令追加到AOF缓冲中
- AOF 缓冲区根据对应的策略向硬盘进行同步操作。
- 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
- 当 Redis 重启时,可以加载 AOF 文件进行数据恢复。
127.0.0.1:6280> set address Beijing OK 127.0.0.1:6280> exit [root@test-1 data]# ls appendonly.aof dump.rdb [root@test-1 data]# cat appendonly.aof REDIS0009 redis-ver6.2.4redis-bitse¡rused-mem`/ 𮤭preambleþmeJerryÿ?Ҫ·ÿWI*2$6SELECT$10*3$3set$7address$7Beijing
开启AOF持久化
修改redis.conf配置文件,默认是appendonly no,将no改为yes即可
#### AOF相关配置 ####
### 开启aof机制
appendonly yes
### aof文件名
appendfilename "appendonly.aof"
### 写入策略,always表示每个写操作都保存到aof文件中,也可以是everysec或no
appendfsync always
### 默认不重写aof文件
no-appendfsync-on-rewrite no
### 保存目录
dir ~/redis/
或者在客户端输入如下命令
127.0.0.1:6280> config set appendonly yes
三种写入策略
appendfsync选项的值 | 效果 |
---|---|
always | 每次有新命令时,就将缓冲区数据写入并同步到 AOF 文件 |
everysec(默认) | 每秒将缓冲区的数据写入并同步到 AOF 文件 |
no | 将缓冲区数据写入AOF 文件,但是同步操作到交给操作系统来处理 |
AOF持久化的实现
-
命令追加
当 AOF 持久化功能处于打开状态时,Redis 在执行完一个写命令之后,会以协议格式(也就是RESP,即 Redis 客户端和服务器交互的通信协议 )将被执行的写命令追加到 Redis 服务端维护的 AOF 缓冲区末尾。
-
文件写入和同步
Redis 每次结束一个事件循环之前,它都会调用
flushAppendOnlyFile
函数,判断是否需要将 AOF 缓存区中的内容写入和同步到 AOF 文件中。flushAppendOnlyFile
函数的行为由 redis.conf 配置中的appendfsync
选项的值来决定
AOF文件重写
为了避免aof文件太大,加载aof文件恢复数据时非常慢
配置文件中的选项no-appendfsync-on-rewrite可以设置是否开启重写,这种方式会在每次fsync时都重写,影响服务器性以,因此默认值为no,不推荐使用。
客户端向服务器发送bgrewriteaof
命令,也可以让服务器进行AOF重写。
修复aof文件
如果Redis服务器宕机,则aof日志文件文件会出格式错误,在重启Redis服务器时,Redis服务器会拒绝载入这个aof文件,可以通过以下步骤修复aof并恢复数据。
redis-check-aof -fix file.aof
AOF的优点
AOF只是追加日志文件,因此对服务器性能影响较小,速度比RDB要快,消耗的内存较少。
AOF的缺点
- 生成的日志文件太大
- 恢复数据比RDB慢
4. RDB和AOF的比较
九、redis主从复制
1. 相关概念
主从复制形态
- 一主一从
- 一主多从
- 从也可以有从
特点
- 异步复制
- 低延迟、高性能
- slave自动重连并尝试成为master的精确副本
- 当
master
服务器与slave
服务器正常连接时,master
服务器会发送数据命令流给slave
服务器,将自身数据的改变复制到slave
服务器。 - 因为各种原因
master
服务器与slave
服务器断开后,slave
服务器在重新连上maste
r服务器时会尝试重新获取断开后未同步的数据即部分同步,或者称为部分复制。 - 如果无法部分同步(比如初次同步),则会请求进行全量同步,这时
master
服务器会将自己的rdb
文件发送给slave
服务器进行数据同步,并记录同步期间的其他写入,再发送给slave
服务器,以达到完全同步的目的,这种方式称为全量复制
工作原理
master服务器记录一个replicationId的伪随机字符串,用于标识当前的数据集版本,并记录一个当前数据集的偏移量offset,不管master是否有配置slave服务器,replication Id和offset会一直记录并成对存在
# 查看replicaiton Id和offset
> info replication
127.0.0.1:6280> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:ab3bd1a7146b5291810cb6bcda94e87d5f2e888c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
当master与slave正常连接时,slave使用PSYNC命令向master发送自己记录的旧master的replication id和offset,而master会计算与slave之间的数据偏移量,并将缓冲区中的偏移数量同步到slave,此时master和slave的数据一致。
而如果slave引用的replication太旧了,master与slave之间的数据差异太大,则master与slave之间会使用全量复制的进行数据同步。
2. 配置redis主从复制
在从服务器slave的配置文件中设置slave选项,或者直接使用slaveof <masterip> <masterport>
命令
使用配置文件配置主从复制
### master 关键配置项
daemonize yes # 要后台运行
bind 0.0.0.0 # 从要能访问
### slave 关键配置项
bind 0.0.0.0
# 5.0.0版本使用replicaof代替slaveof,不过slaveof仍然可以使用
replicaof 192.168.5.30 6280
masterauth Charramma123456 # master的密码
分别使用info replication
查看主从的信息
############### master
127.0.0.1:6280> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.5.53,port=6280,state=online,offset=224,lag=0
master_failover_state:no-failover
master_replid:362388dad40ba6fa2b60d292ed85a9384d8f213d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:224
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:224
############### slave
127.0.0.1:6280> info replication
# Replication
role:slave
master_host:192.168.5.30
master_port:6280
master_link_status:up
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:28
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:362388dad40ba6fa2b60d292ed85a9384d8f213d
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:28
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:28
slaveof命令配置、撤销主从
#### 开启主从
# 如果master有requirepass配置(密码验证),且slave的bind配置项设置了固定ip
config set bind 0.0.0.0
config set masterauth <masterip> <masterpass>
# 返回永远是OK
slaveof <masterip> <masterport>
#### 撤销
slaveof no one
注意
-
如果master和slave在配置主从前有同名key,配置主从后将会用master的value替换slave的value
-
slave无法进行写操作
十、redis高可用
redis有四种高可用的方式
-
持久化
数据备份,保证数据不会因为进程退出而丢失
-
复制
缺陷是故障恢复无法自动化、写操作无法负载均衡、存储能力受到单机的限制。
-
哨兵
在复制的基础上实现了自动化的故障恢复
缺陷是 写操作 无法 负载均衡,存储能力 受到 单机 的限制。
-
集群
解决了 写操作 无法 负载均衡 以及 存储能力 受到 单机限制 的问题,实现了较为 完善 的 高可用方案。
1. 哨兵(Sentinel)
https://juejin.cn/post/6844903663362637832
Sentinel是一个管理多个redis实例的工具,可以实现对redis的监控、通知、自动故障转移
基本名词:
基本名词 | 逻辑结构 | 物理结构 |
---|---|---|
Redis数据节点 | 主节点和从节点 | 主节点和从节点的进程 |
主节点(master) | Redis主数据库 | 一个独立的Redis进程 |
从节点(slave) | Redis从数据库 | 一个独立的Redis进程 |
Sentinel节点 | 监控Redis数据节点 | 一个独立的Sentinel进程 |
Sentinel节点集合 | 若干Sentinel节点的抽象组合 | 若干Sentinel节点进程 |
Redis Sentinel | Redis高可用实现方案 | Sentinel节点集合和Redis数据节点进程 |
应用客户端 | 泛指一个或多个客户端 | 一个或者多个客户端进程或者线程 |
架构:
Sentinel主要功能
-
监控
默认情况下,每个
Sentinel
节点会以 每秒一次 的频率对Redis
节点和 其它 的Sentinel
节点发送PING
命令,并通过节点的 回复 来判断节点是否在线。- 没有收到目标节点的有效回复,判断该节点为主观下线
- 通过询问其他Sentinel节点,如果有超过
<quorum>
个数的节点判定主节点不可达,则该节点判断主节点为客观下线(客观下线只适用于主节点)
-
通知
当被监控的某个 Redis 服务器出现问题,Sentinel 通过 API 脚本 向 管理员 或者其他的 应用程序 发送通知。
-
自动故障转移
主节点不能正常工作时,自动将一个从节点升级为新的主节点,并将其他从节点指向新的主节点
-
配置提供者
客户端应用 在初始化时连接的是
Sentinel
节点集合,从中获取 主节点 的信息
工作原理
每个 Sentinel
节点都需要 定期执行 以下任务:
- 每个
Sentinel
以 每秒钟 一次的频率,向它所知的 主服务器、从服务器 以及其他Sentinel
实例 发送一个PING
命令。 - 如果一个 实例(
instance
)距离 最后一次 有效回复PING
命令的时间超过down-after-milliseconds
所指定的值,那么这个实例会被Sentinel
标记为 主观下线 - 如果一个 主服务器 被标记为 主观下线,那么正在 监视 这个 主服务器 的所有
Sentinel
节点,要以 每秒一次 的频率确认 主服务器 的确进入了 主观下线 状态。 - 如果一个 主服务器 被标记为 主观下线,并且有 足够数量 的
Sentinel
(至少要达到 配置文件 指定的数量)在指定的 时间范围 内同意这一判断,那么这个 主服务器 被标记为 客观下线。 Sentinel
和其他Sentinel
协商 主节点 的状态,如果 主节点 处于SDOWN
状态,则投票自动选出新的 主节点。将剩余的 从节点 指向 新的主节点 进行 数据复制。- 当没有足够数量的
Sentinel
同意 主服务器 下线时, 主服务器 的 客观下线状态 就会被移除。当 主服务器 重新向Sentinel
的PING
命令返回 有效回复 时,主服务器 的 主观下线状态 就会被移除。
搭建Sentinel
可以在一台主机上进行:
-
三个redis_port.conf,对应这master、slave1、slave2三个redis节点的启动配置
# -------- master redis_12138.conf ------------ daemonize yes pidfile /usr/local/redis/logs/redis-12138.pid logfile /usr/local/redis/logs/redis-12138.log port 12138 bind 0.0.0.0 timeout 300 databases 16 dbfilename dump-12138.db dir /usr/local/redis/data/ masterauth 123456 requirepass 123456 # -------- slave1 redis_12139.conf------------ daemonize yes pidfile /usr/local/redis/logs/redis-12139.pid logfile /usr/local/redis/logs/redis-12139.log port 12139 bind 0.0.0.0 timeout 300 databases 16 dbfilename dump-12139.db dir /usr/local/redis/data/ masterauth 123456 requirepass 123456 slaveof 127.0.0.1 12138 # -------- slave2 redis_12140.conf ------------ daemonize yes pidfile /usr/local/redis/logs/redis-12140.pid logfile /usr/local/redis/logs/redis-12140.log port 12140 bind 0.0.0.0 timeout 300 databases 16 dbfilename dump-12140.db dir /usr/local/redis/data/ masterauth Charramma123# requirepass Charramma123# slaveof 127.0.0.1 12138
如果要做 自动故障转移,建议所有的
redis.conf
都设置masterauth
。因为 自动故障 只会重写 主从关系,即slaveof
,不会自动写入masterauth
。如果Redis
原本没有设置密码,则可以忽略。 -
顺序启动master\slave1\slave2
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis_12138.conf /usr/local/redis/bin/redis-server /usr/local/redis/conf/redis_12139.conf /usr/local/redis/bin/redis-server /usr/local/redis/conf/redis_12140.conf
-
三份sentinel_port.conf,对应三个redis节点的哨兵配置
# ---------- sentinel_22138.conf -------------- protected-mode no bind 0.0.0.0 port 22138 daemonize yes logfile /usr/local/redis/logs/sentinel-22138.log sentinel monitor master 127.0.0.1 12138 2 sentinel down-after-milliseconds master 5000 sentinel failover-timeout master 180000 sentinel parallel-syncs master 1 sentinel auth-pass master 123456 # ---------- sentinel_22139.conf -------------- protected-mode no bind 0.0.0.0 port 22139 daemonize yes logfile /usr/local/redis/logs/sentinel-22139.log sentinel monitor master 127.0.0.1 12138 2 sentinel down-after-milliseconds master 5000 sentinel failover-timeout master 180000 sentinel parallel-syncs master 1 sentinel auth-pass master 123456 # ---------- sentinel_22140.conf -------------- protected-mode no bind 0.0.0.0 port 22140 daemonize yes logfile /usr/local/redis/logs/sentinel-22140.log sentinel monitor master 127.0.0.1 12138 2 sentinel down-after-milliseconds master 5000 sentinel failover-timeout master 180000 sentinel parallel-syncs master 1 sentinel auth-pass master 123456
-
启动三个sentinel节点
/usr/local/redis/bin/redis-server /usr/local/redis/conf/sentinel_22138.conf --sentinel /usr/local/redis/bin/redis-server /usr/local/redis/conf/sentinel_22139.conf --sentinel /usr/local/redis/bin/redis-server /usr/local/redis/conf/sentinel_22140.conf --sentinel
查看sentinel的日志,可以看到三个sentinel的
Sentinel ID
,通过Sentinel ID 把自己加入到sentinel集群中再打开sentinel的配置文件,发现发生了变化
-
sentinel客户端命令
# 显示所有被监控的主节点以及他们的状态 > SENTINEL masters # 显示指定主节点信息 > SENTINEL master <master_name> # 显示指定主节点的所有从节点的状态 > SENTINEL slaves <master_name> # 返回指定主节点的ip地址和端口,如果正在进行failover或者failover已完成,将会显示被提升为主节点的从节点的IP地址和端口 > SENTINEL get-master-addr-by-name <master_name> # 重置匹配的所有主节点的状态信息,清除它之前的 状态信息,以及 从节点 的信息。 > SENTINEL reset <pattern> # 强制当前Sentinel节点执行failover,并且不需要得到其他Sentinel节点的同意,但是 failover 后会将 最新的配置 发送给其他 Sentinel 节点。 # 发生主节点变更的时候,sentinel的配置文件也会刷新,redis的配置文件也会刷新 SENTINEL failover <master_name>
###### Sentinel相关配置项解释 #------------------------------------------------------- # 哨兵sentinel实例运行的端口,默认26379 port 26379 # 哨兵sentinel的工作目录 dir ./ # 哨兵sentinel监控的redis主节点的 ## ip:主机ip地址 ## port:哨兵端口号 ## master-name:可以自己命名的主节点名字(只能由字母A-z、数字0-9 、这三个字符".-_"组成。) ## quorum:当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了 # sentinel monitor <master-name> <ip> <redis-port> <quorum> # 如果要监视多个主从集群,这条配置可以重复添加,注意master-name不能一样 sentinel monitor mymaster 127.0.0.1 6379 2 # 当在Redis实例中开启了requirepass <foobared>,所有连接Redis实例的客户端都要提供密码。 # sentinel auth-pass <master-name> <password> sentinel auth-pass mymaster 123456 # 指定主节点应答哨兵sentinel的最大时间间隔,超过这个时间,哨兵主观上认为主节点下线,默认30秒 # sentinel down-after-milliseconds <master-name> <milliseconds> sentinel down-after-milliseconds mymaster 30000 # 指定了在发生failover主备切换时,最多可以有多少个slave同时对新的master进行同步。这个数字越小,完成failover所需的时间就越长;反之,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为1,来保证每次只有一个slave,处于不能处理命令请求的状态。 # sentinel parallel-syncs <master-name> <numslaves> sentinel parallel-syncs mymaster 1 # 故障转移的超时时间failover-timeout,默认三分钟,可以用在以下这些方面: ## 1. 同一个sentinel对同一个master两次failover之间的间隔时间。 ## 2. 当一个slave从一个错误的master那里同步数据时开始,直到slave被纠正为从正确的master那里同步数据时结束。 ## 3. 当想要取消一个正在进行的failover时所需要的时间。 ## 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来同步数据了 # sentinel failover-timeout <master-name> <milliseconds> sentinel failover-timeout mymaster 180000 # 当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本。一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。 # 对于脚本的运行结果有以下规则: ## 1. 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10。 ## 2. 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。 ## 3. 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。 # sentinel notification-script <master-name> <script-path> sentinel notification-script mymaster /var/redis/notify.sh # 这个脚本应该是通用的,能被多次调用,不是针对性的。 # sentinel client-reconfig-script <master-name> <script-path> sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
2. 集群
Redis Sentinal着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
特性:
- 高可用
- 可扩展性
- 分布式
- 容错
集群方案:
-
官方集群方案 Redis Cluster
Redis cluster是一种服务器sharding技术,采用slot(槽)的概念,一共分成16384个槽,对于每个进入redis的键值对,根据key进行散列,分配到这16384个slot中的某一个。
redis集群中的每一个node复制分摊这些slot,动态增加或减少node节点时,需要将这些slot再分配,这一过程需要人工介入。
集群搭建
-
至少三个master,每个master至少有一个slave,也就是至少需要6个node
127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
-
六个配置文件
daemonize yes bing 127.0.0.1 dir /usr/local/redis/data/redis_7000 pidfile /usr/local/redis/logs/redis_7000.pid logfile /usr/local/redis/logs/redis_7000.log port 7000 # start cluster cluster-enabled yes cluster-config-file /usr/local/redis/conf/node_7000.conf cluster-node-timeout 10000 appendonly yes # 其他几个配置文件除了端口不同,其他都可以一样
-
安装ruby环境
搭建集群需要使用到官方提供的ruby脚本。需要安装ruby的环境。
yum install ruby rubygems -y # 升级ruby yum install centos-release-scl-rh # 加源 yum install rh-ruby23 -y scl enable ru-ruby23 bash gem install redis # redis-trib.rb这个文件在redis的源码包里 cp /tmp/redis-6.2.4/src/redis-trib.rb /usr/local/redis/
-
启动redis
-
关联集群节点
redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 # 6.2.4执行上面这条命令好像不行,推荐下面这条 redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1 # 查看集群 [root@test-3 bin]# redis-cli -p 7000 127.0.0.1:7000> cluster nodes dc14abf5c42edddd2ac19d32f01a71793b572b07 127.0.0.1:7004@17004 slave 721b63435e1c7a7581d1fba2fa50420e8a05187f 0 1623925888000 3 connected a1e48b92c5d0c9bb521e291ecbc4f15150cae839 127.0.0.1:7003@17003 slave 3845d8965294bb45660e5f8e86b4aa129a3324e8 0 1623925888091 2 connected 721b63435e1c7a7581d1fba2fa50420e8a05187f 127.0.0.1:7002@17002 master - 0 1623925889000 3 connected 10923-16383 86428351f7e98775e4b1d6c54f2ef4729b2c63ce 127.0.0.1:7000@17000 myself,master - 0 1623925887000 1 connected 0-5460 2f3a601ad1bc9c3de9c52f7e706ff98156fce674 127.0.0.1:7005@17005 slave 86428351f7e98775e4b1d6c54f2ef4729b2c63ce 0 1623925890104 1 connected 3845d8965294bb45660e5f8e86b4aa129a3324e8 127.0.0.1:7001@17001 master - 0 1623925889097 2 connected 5461-10922 # 检查集群完整性 redis-cli --cluster check 127.0.0.1:7000 # 看到下面这个说明没啥毛病 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
十一、HyperLogLog
redis2.8.9版本添加
一种基数统计算法,在输入元素的数量或体积很大的时候,计算基数所需的空间总是稳定的并且是很小的。
只会根据输入元素来计算基数,不会存储元素本身
什么是基数
如数据集{1,3,5,7,5,7,8},那么这个数据集的基数集为{1,3,5,7,8},基数集长度5即为基数。
基数估计:在误差可接受的范围内快速计算基数
实例
127.0.0.1:6280> PFADD name Tom
(integer) 1
127.0.0.1:6280> PFADD name Jerry
(integer) 1
127.0.0.1:6280> PFADD name Tom
(integer) 0
127.0.0.1:6280> PFADD name Taff
(integer) 1
127.0.0.1:6280> PFCOUNT name
(integer) 3
相关命令
命令 | 描述 |
---|---|
PFADD key element | 将指定的元素添加到HyperLogLog中 |
PFCOUNT key | 返回给定HyperLogLog的基数估算值 |
PFMERGE destkey sourcekey | 将多个HyperLogLog合并为一个HyperLogLog |
red