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请求过程:

  1. 客户端向服务端发送请求,监听socket返回
  2. 服务端处理命令,返回给客户端

依次向服务端发送多个请求,如果前一个请求在服务器未得到响应,后一个请求会等待

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"

sub

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-scriptLua 脚本内容。
  • numkeys:表示的是 Lua 脚本中需要用到多少个 key,如果没用到则写 0
  • key [key ...]:将 key 作为参数按顺序传递到 Lua 脚本,numkeys0 时则可省略。
  • argLua 脚本中用到的参数,如果没有可省略。

示例:

# 一个不带参数的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会话

  1. 前提:本地有一个Lua脚本,假设叫做test.lua

    local foo = redis.call('ping')
    return foo
    
  2. 进入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)

说明:

  1. 当某个客户端向服务端发起debugging会话的时候,并不会阻塞服务端,即服务端仍能正常的处理请求,而且也能同时处理多个debugging会话

    --ldb-sync-mode参数指定LDB为同步模式,在该工作模式下调整Lua脚本会阻塞服务器,debug的过程中服务端不会处理其他请求,并且不会对数据做回滚操作

  2. debugging会话结束的时候,Lua脚本中对redis数据的所有修改都会回滚

  3. 使用restart重新开启debug会话时,每次的执行效果都相同(回滚了)

  4. LDB在设计命令的时候,每个不同的命令的首字母都不一样,因此可以使用首字母缩写的方式代替命令全拼,如print命令,可以使用其首字母p代替

七、redis事务

Redis 只提供了简单的事务功能。本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。命令的执行过程是是原子顺序执行的,但是不能保证原子性

原子性:要么全部执行成功,要么全部执行失败

事务从开始到结束的三个阶段:

  1. 事务开始
  2. 命令入队
  3. 事务执行

相关命令:

命令 说明
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提供了两种数据持久化方式:RDBAOF

我发现退出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-bits󿿀򳨭e¡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持久化的实现

  1. 命令追加

    当 AOF 持久化功能处于打开状态时,Redis 在执行完一个写命令之后,会以协议格式(也就是RESP,即 Redis 客户端和服务器交互的通信协议 )将被执行的写命令追加到 Redis 服务端维护的 AOF 缓冲区末尾。

  2. 文件写入和同步

    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的比较

img

九、redis主从复制

1. 相关概念

主从复制形态

  • 一主一从
  • 一主多从
  • 从也可以有从

特点

  • 异步复制
  • 低延迟、高性能
  • slave自动重连并尝试成为master的精确副本
  1. master服务器与slave服务器正常连接时,master服务器会发送数据命令流给slave服务器,将自身数据的改变复制到slave服务器。
  2. 因为各种原因master服务器与slave服务器断开后,slave服务器在重新连上master服务器时会尝试重新获取断开后未同步的数据即部分同步,或者称为部分复制。
  3. 如果无法部分同步(比如初次同步),则会请求进行全量同步,这时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. 持久化

    数据备份,保证数据不会因为进程退出而丢失

  2. 复制

    缺陷是故障恢复无法自动化、写操作无法负载均衡、存储能力受到单机的限制。

  3. 哨兵

    在复制的基础上实现了自动化的故障恢复

    缺陷是 写操作 无法 负载均衡存储能力 受到 单机 的限制。

  4. 集群

    解决了 写操作 无法 负载均衡 以及 存储能力 受到 单机限制 的问题,实现了较为 完善高可用方案

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数据节点进程
应用客户端 泛指一个或多个客户端 一个或者多个客户端进程或者线程

架构:

img

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 同意 主服务器 下线时, 主服务器 的 客观下线状态 就会被移除。当 主服务器 重新向 SentinelPING 命令返回 有效回复 时,主服务器 的 主观下线状态 就会被移除。

搭建Sentinel

可以在一台主机上进行:

  1. 三个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 原本没有设置密码,则可以忽略。

  2. 顺序启动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
    
  3. 三份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
    
  4. 启动三个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集群中

    image-20210617101537233

    再打开sentinel的配置文件,发现发生了变化

    image-20210617102256415

  5. 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进行分片存储。

特性:

  • 高可用
  • 可扩展性
  • 分布式
  • 容错

集群方案:

  1. 官方集群方案 Redis Cluster

    Redis cluster是一种服务器sharding技术,采用slot(槽)的概念,一共分成16384个槽,对于每个进入redis的键值对,根据key进行散列,分配到这16384个slot中的某一个。

    redis集群中的每一个node复制分摊这些slot,动态增加或减少node节点时,需要将这些slot再分配,这一过程需要人工介入。

集群搭建

  1. 至少三个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
    
  2. 六个配置文件

    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
    
    # 其他几个配置文件除了端口不同,其他都可以一样
    
  3. 安装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/
    
  4. 启动redis

  5. 关联集群节点

    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

posted @ 2022-08-08 22:53  Charramma  阅读(37)  评论(0编辑  收藏  举报