Redis

一、redis知识点学习文档:

https://blog.csdn.net/m0_65621281/article/details/139530223

二、redis单节点安装

1.下载redis-4.0.0.tar.gz安装包

[root@nginx1 ~]# cd /wwq/packages/
[root@nginx1 ~]# wget http://download.redis.io/releases/redis-3.2.2.tar.gz

2.解压redis

[root@nginx1 redis-3.2.2]# tar -zxvf redis-3.2.2.tar.gz 

3.编译安装redis

[root@nginx1 redis-3.2.2]# make && make install

若出现下图所示,则说明编译成功。

4.创建redis节点

[root@nginx1 redis-3.2.2]# cd ..                                  //返回redis目录
[root@nginx1 packages]# mkdir redis_cluster                //创建redis_cluster目录
[root@nginx1 packages]# cd redis_cluster                    //进去redis_cluster目录
[root@nginx1 redis_cluster]# mkdir 8687                     //创建8687目录
[root@nginx1 redis_cluster]# cd ..                          //返回redis_cluster目录
[root@nginx1 packages]# cd redis-3.2.2/                     //进入redis-4.0.0目录       
[root@nginx1 redis-3.2.2]# cp redis.conf ../redis_cluster/8687/     // 拷贝redis.conf到8687目录下

5.修改刚拷贝的配置文件

[root@nginx1 redis-3.2.2]# vim redis.conf                  
port  8687                             //端口8687        
bind 登录的IP                           //默认ip为127.0.0.1 需要改为其他节点机器可访问的ip 否则创建集群时无法访问对应的端口,无法创建集群
daemonize    yes                       //redis后台运行
pidfile  /var/run/redis_8687.pid       //pidfile文件对应8687
cluster-enabled  yes                   //开启集群  把注释#去掉
cluster-config-file  nodes_8687.conf   //集群的配置  配置文件首次启动自y动生成 8687
cluster-node-timeout  15000            //请求超时  默认15秒,可自行设置
appendonly  yes                        //aof日志开启  有需要就开启,它会每次写操作都记录一条日志

6.启动节点

[root@nginx1 ~]# cd /wwq/packages/
[root@nginx1 packages]# redis-3.2.2/src/redis-server ./redis_cluster/8687/redis.conf  

7.测试是否启动

[root@nginx1 ~]# ps -ef|grep redis

三、redis Cluster模式安装

前提:已经安装好redis单机版。

当集群中超过或等于1/2节点不可用时,整个集群不可用。为了搭建稳定集群,都采用奇数节点。

1 .复制redis配置文件

[root@nginx1 redis_cluster]# cd /wwq/packages/redis_cluster/8687/
[root@nginx1 8687]# cp redis.conf ../redis-7001.conf
[root@nginx1 8687]# cp redis.conf ../redis-7002.conf
[root@nginx1 8687]# cp redis.conf ../redis-7003.conf
[root@nginx1 8687]# cp redis.conf ../redis-7004.conf
[root@nginx1 8687]# cp redis.conf ../redis-7005.conf
[root@nginx1 8687]# cp redis.conf ../redis-7006.conf

新复制的5个配置文件都需要需改三处。

2.修改内容

[root@nginx1 8687]# cd ../
[root@nginx1 redis_cluster]# ls
8687  redis-7001.conf  redis-7002.conf  redis-7003.conf  redis-7004.conf  redis-7005.conf  redis-7006.conf
[root@nginx1 redis_cluster]# vim redis-7001.conf

需要修改如下:

port 7001
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 15000
# appendonly yes 如果开启aof默认,需要修改为yes。如果使用rdb,此处不需要修改
daemonize yes
protected-mode no
pidfile /var/run/redis_7001.pid

例如nodes-7002.conf中需要把所有7001都换成7002。

可以使用 :%s/7001/7002/g 进行全局修改。

4 .启动6个redis

可以使用redis-server结合6个配置文件进行启动5个实例。

执行之前一定要先删除dump.rdb

[root@nginx1 src]# rm -f dump.rdb
[root@nginx1 ~]# cd /wwq/packages/redis-3.2.2/src/
[root@nginx1 src]# vim startup.sh

./redis-server ../../redis_cluster/redis-7001.conf
./redis-server ../../redis_cluster/redis-7002.conf
./redis-server ../../redis_cluster/redis-7003.conf
./redis-server ../../redis_cluster/redis-7004.conf
./redis-server ../../redis_cluster/redis-7005.conf
./redis-server ../../redis_cluster/redis-7006.conf

[root@nginx1 src]# chmod a+x startup.sh
[root@nginx1 src]# ./startup.sh

5.查看启动状态

[root@nginx1 src]# ps aux|grep redis

6. 建立集群

在redis3的时候需要借助ruby脚本实现集群。在redis5中可以使用自带的redis-cli实现集群功能,比redis3的时候更加方便。
其中 –cluster-replicas 1 表示 一个master后有几个slave,1表示一个slave。
建议配置静态ip,ip改变集群失效
Redis 3.x 使用 redis-trib.rb(在 src 目录)来执行集群创建:

使用 RVM 安装 Ruby 2.7+ 的完整命令

 # 安装依赖
yum install -y curl gpg gcc gcc-c++ make

# 安装 GPG keys
gpg2 --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 
409B6B1796C275462A1703113804BB82D39DC0E3 
7D2BAF1CF37B13E2069D6956105BD0E739499BDB

# 安装 RVM
curl -sSL https://get.rvm.io | bash -s stable

# 加载 RVM 环境
source /etc/profile.d/rvm.sh

# 安装 Ruby 2.7
rvm install 2.7

# 使用 Ruby 2.7
rvm use 2.7 --default

# 安装 redis gem
gem install redis
[root@nginx1 src]# ruby redis-trib.rb create --replicas 1 
>   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  127.0.0.1:7006

>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:7001
127.0.0.1:7002
127.0.0.1:7003
Adding replica 127.0.0.1:7004 to 127.0.0.1:7001
Adding replica 127.0.0.1:7005 to 127.0.0.1:7002
Adding replica 127.0.0.1:7006 to 127.0.0.1:7003
M: 0011f4dab1016b66b72abaea6ced3cc03fd26d30 127.0.0.1:7001
   slots:0-5460 (5461 slots) master
M: be2a07525fe5d0db16fa5df8816e1ee962b9a7c7 127.0.0.1:7002
   slots:5461-10922 (5462 slots) master
M: 4c8a0607abb96f335712bfadfc9d8bd24759f7fc 127.0.0.1:7003
   slots:10923-16383 (5461 slots) master
S: db5b3d85427d995e0a6bc7e011c74e2bee4909f6 127.0.0.1:7004
   replicates 0011f4dab1016b66b72abaea6ced3cc03fd26d30
S: 5f38e3990be54437aae18b5bf3e6bce711b55f24 127.0.0.1:7005
   replicates be2a07525fe5d0db16fa5df8816e1ee962b9a7c7
S: c28ec18dd50c8810cf3df8327f0358c893ccfb97 127.0.0.1:7006
   replicates 4c8a0607abb96f335712bfadfc9d8bd24759f7fc
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join....
>>> Performing Cluster Check (using node 127.0.0.1:7001)
M: 0011f4dab1016b66b72abaea6ced3cc03fd26d30 127.0.0.1:7001
   slots:0-5460 (5461 slots) master
M: be2a07525fe5d0db16fa5df8816e1ee962b9a7c7 127.0.0.1:7002
   slots:5461-10922 (5462 slots) master
M: 4c8a0607abb96f335712bfadfc9d8bd24759f7fc 127.0.0.1:7003
   slots:10923-16383 (5461 slots) master
M: db5b3d85427d995e0a6bc7e011c74e2bee4909f6 127.0.0.1:7004
   slots: (0 slots) master
   replicates 0011f4dab1016b66b72abaea6ced3cc03fd26d30
M: 5f38e3990be54437aae18b5bf3e6bce711b55f24 127.0.0.1:7005
   slots: (0 slots) master
   replicates be2a07525fe5d0db16fa5df8816e1ee962b9a7c7
M: c28ec18dd50c8810cf3df8327f0358c893ccfb97 127.0.0.1:7006
   slots: (0 slots) master
   replicates 4c8a0607abb96f335712bfadfc9d8bd24759f7fc
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

7. 测试

[root@nginx1 src]# redis-cli -c -p 7001
127.0.0.1:7001> set age 18
OK
127.0.0.1:7001> get age
"18"
127.0.0.1:7001> cluster nodes

8 .编写关闭脚本

[root@nginx1 src]# vim stop.sh

./redis-cli -p 7001 shutdown
./redis-cli -p 7002 shutdown
./redis-cli -p 7003 shutdown
./redis-cli -p 7004 shutdown
./redis-cli -p 7005 shutdown
./redis-cli -p 7006 shutdown

[root@nginx1 src]# chmod a+x stop.sh

四、redis常用命令学习

1.连接与服务器命令

在开始操作数据前,先掌握这些基础命令:

# 1. 测试连接
> PING
PONG

# 2. 认证(如果设置了密码)
> AUTH your_password
OK

# 3. 切换数据库(Redis默认有16个数据库,编号0-15)
> SELECT 1
OK

# 4. 获取服务器信息
> INFO
# Server
redis_version:7.0.5
...

# 5. 获取当前数据库中键的数量
> DBSIZE
(integer) 42

# 6. 获取最后一次成功执行持久化的时间点
> LASTSAVE
(integer) 1675932539

# 7. 获取服务器运行时间(秒)
> INFO server | grep uptime_in_seconds
uptime_in_seconds:86400

2.键(Key)的通用操作

无论使用什么数据类型,这些命令都可以操作键:

# 1. 查找所有匹配的键(生产环境慎用)
> KEYS user:*
1) "user:1001"
2) "user:1002"

# 2. 迭代数据库中的键(推荐替代KEYS)
> SCAN 0 MATCH user:* COUNT 10
1) "32"  # 下一个游标位置
2) 1) "user:1001"
   2) "user:1002"

# 3. 检查键是否存在
> EXISTS user:1001
(integer) 1

# 4. 获取键的类型
> TYPE user:1001
hash

# 5. 删除键
> DEL user:1002
(integer) 1

# 6. 异步删除键(大键推荐)
> UNLINK big_list_key
(integer) 1

# 7. 重命名键
> RENAME user:1001 user:1001:profile
OK

# 8. 设置键的过期时间(秒)
> EXPIRE session:token123 1800
(integer) 1

# 9. 设置键的过期时间点
> EXPIREAT cache:item123 1675932600
(integer) 1

# 10. 获取键的剩余生存时间(秒)
> TTL session:token123
(integer) 1795

# 11. 移除键的过期时间
> PERSIST session:token123
(integer) 1

# 12. 随机返回一个键
> RANDOMKEY
"user:1003"

3.字符串(String)命令精选

字符串是Redis最基础的数据类型,掌握这些命令是必须的:

# 1. 设置字符串值
> SET username "alice"
OK

# 2. 获取字符串值
> GET username
"alice"

# 3. 设置值并返回旧值
> GETSET username "bob"
"alice"

# 4. 同时设置多个键值
> MSET first_name "John" last_name "Doe" age "30"
OK

# 5. 同时获取多个键值
> MGET first_name last_name age
1) "John"
2) "Doe"
3) "30"

# 6. 键不存在时设置值(常用于分布式锁)
> SETNX lock:resource1 "acquired"
(integer) 1

# 7. 设置值的同时设置过期时间
> SET token "auth-token-value" EX 3600
OK

# 8. 递增整数值
> SET counter 10
OK
> INCR counter
(integer) 11

# 9. 增加指定的整数
> INCRBY counter 5
(integer) 16

# 10. 递增浮点数
> SET price 19.99
OK
> INCRBYFLOAT price 0.5
"20.49"

# 11. 递减整数
> DECR counter
(integer) 15

# 12. 减少指定的整数
> DECRBY counter 5
(integer) 10

# 13. 获取字符串长度
> STRLEN username
(integer) 3

# 14. 追加内容到字符串
> APPEND username "-admin"
(integer) 8
> GET username
"bob-admin"

4.哈希(Hash)命令精选

哈希是存储对象的理想选择,这些命令帮你高效操作哈希:

# 1. 设置哈希字段
> HSET user:1001 name "张三" age 25 email "zhangsan@example.com"
(integer) 3

# 2. 获取哈希字段
> HGET user:1001 name
"张三"

# 3. 获取多个哈希字段
> HMGET user:1001 name age
1) "张三"
2) "25"

# 4. 获取所有字段和值
> HGETALL user:1001
1) "name"
2) "张三"
3) "age"
4) "25"
5) "email"
6) "zhangsan@example.com"

# 5. 检查字段是否存在
> HEXISTS user:1001 phone
(integer) 0

# 6. 获取所有字段名
> HKEYS user:1001
1) "name"
2) "age"
3) "email"

# 7. 获取所有字段值
> HVALS user:1001
1) "张三"
2) "25"
3) "zhangsan@example.com"

# 8. 获取哈希字段数量
> HLEN user:1001
(integer) 3

# 9. 递增哈希字段的整数值
> HINCRBY user:1001 age 1
(integer) 26

# 10. 递增哈希字段的浮点值
> HSET product:1234 price 99.5
(integer) 1
> HINCRBYFLOAT product:1234 price 0.5
"100"

# 11. 字段不存在时设置值
> HSETNX user:1001 address "北京市"
(integer) 1

5.列表(List)命令精选

列表支持队列和栈操作,这些命令让你轻松处理有序元素集合:

# 1. 向列表左端添加元素
> LPUSH recent_news "新闻标题1" "新闻标题2"
(integer) 2

# 2. 向列表右端添加元素
> RPUSH task_queue "任务1" "任务2"
(integer) 2

# 3. 获取列表指定范围元素
> LRANGE recent_news 0 -1
1) "新闻标题2"
2) "新闻标题1"

# 4. 从列表左端弹出元素
> LPOP recent_news
"新闻标题2"

# 5. 从列表右端弹出元素
> RPOP task_queue
"任务2"

# 6. 获取列表长度
> LLEN task_queue
(integer) 1

# 7. 按索引获取元素
> LINDEX recent_news 0
"新闻标题1"

# 8. 按索引设置元素
> LSET recent_news 0 "更新的新闻标题"
OK

# 9. 在指定元素前/后插入元素
> RPUSH colors "red" "black"
(integer) 2
> LINSERT colors BEFORE "black" "white"
(integer) 3
> LRANGE colors 0 -1
1) "red"
2) "white"
3) "black"

# 10. 保留列表指定范围元素
> RPUSH messages "msg1" "msg2" "msg3" "msg4" "msg5"
(integer) 5
> LTRIM messages 0 2  # 只保留前3条消息
OK
> LRANGE messages 0 -1
1) "msg1"
2) "msg2"
3) "msg3"

# 11. 阻塞式弹出(超时5秒)
> BLPOP queue:notifications 5
(nil)  # 5秒内没有元素可弹出

6.集合(Set)命令精选

集合用于处理唯一元素的无序集合,支持丰富的集合运算:

# 1. 添加元素到集合
> SADD interests "音乐" "电影" "阅读"
(integer) 3

# 2. 获取集合所有成员
> SMEMBERS interests
1) "音乐"
2) "电影"
3) "阅读"

# 3. 判断元素是否是集合成员
> SISMEMBER interests "音乐"
(integer) 1

# 4. 获取集合成员数量
> SCARD interests
(integer) 3

# 5. 移除集合成员
> SREM interests "阅读"
(integer) 1

# 6. 随机获取成员
> SRANDMEMBER interests 2
1) "电影"
2) "音乐"

# 7. 随机弹出成员
> SPOP interests
"音乐"

# 8. 计算集合交集
> SADD set1 "a" "b" "c"
(integer) 3
> SADD set2 "b" "c" "d"
(integer) 3
> SINTER set1 set2
1) "b"
2) "c"

# 9. 计算集合并集
> SUNION set1 set2
1) "a"
2) "b"
3) "c"
4) "d"

# 10. 计算集合差集
> SDIFF set1 set2
1) "a"

# 11. 将交集/并集/差集结果存储
> SINTERSTORE result set1 set2
(integer) 2
> SMEMBERS result
1) "b"
2) "c"

7.有序集合(Sorted Set)命令精选

有序集合适合处理排序场景,如排行榜、优先级队列:

# 1. 添加带分数的成员
> ZADD leaderboard 100 "player1" 200 "player2" 150 "player3"
(integer) 3

# 2. 获取成员分数
> ZSCORE leaderboard "player2"
"200"

# 3. 增加成员分数
> ZINCRBY leaderboard 50 "player1"
"150"

# 4. 获取排名(从低到高)
> ZRANK leaderboard "player1"
(integer) 1

# 5. 获取排名(从高到低)
> ZREVRANK leaderboard "player2"
(integer) 0

# 6. 按索引获取成员(从低到高)
> ZRANGE leaderboard 0 -1
1) "player1"
2) "player3"
3) "player2"

# 7. 按索引获取成员和分数
> ZRANGE leaderboard 0 -1 WITHSCORES
1) "player1"
2) "150"
3) "player3"
4) "150"
5) "player2"
6) "200"

# 8. 按索引获取成员(从高到低)
> ZREVRANGE leaderboard 0 -1
1) "player2"
2) "player3"
3) "player1"

# 9. 按分数范围获取成员
> ZRANGEBYSCORE leaderboard 100 180
1) "player1"
2) "player3"

# 10. 获取集合大小
> ZCARD leaderboard
(integer) 3

# 11. 获取指定分数范围的成员数量
> ZCOUNT leaderboard 150 200
(integer) 3

# 12. 移除指定成员
> ZREM leaderboard "player3"
(integer) 1

# 13. 移除指定排名范围的成员
> ZREMRANGEBYRANK leaderboard 0 0  # 移除分数最低的成员
(integer) 1

8.Bitmap和HyperLogLog命令

Bitmap和HyperLogLog是Redis的高级特性,解决了常见的统计问题:

# 1. 设置位图的位
> SETBIT user:active:20230701 123 1
(integer) 0

# 2. 获取位图的位
> GETBIT user:active:20230701 123
(integer) 1

# 3. 统计位图中1的个数
> BITCOUNT user:active:20230701
(integer) 1

# 4. 对多个位图执行按位操作
> SETBIT user:active:20230701 123 1
(integer) 0
> SETBIT user:active:20230702 123 1
(integer) 0
> BITOP AND user:active:both_days user:active:20230701 user:active:20230702
(integer) 16
> BITCOUNT user:active:both_days
(integer) 1

# 5. 添加元素到HyperLogLog
> PFADD visitors:20230701 "user1" "user2" "user3"
(integer) 1

# 6. 估计HyperLogLog的基数
> PFCOUNT visitors:20230701
(integer) 3

# 7. 合并多个HyperLogLog
> PFADD visitors:20230702 "user2" "user4"
(integer) 1
> PFMERGE visitors:combined visitors:20230701 visitors:20230702
OK
> PFCOUNT visitors:combined
(integer) 4

9.地理空间(GEO)命令

GEO命令用于存储和查询地理位置数据:

# 1. 添加地理位置
> GEOADD locations 116.39 39.91 "北京" 121.47 31.23 "上海" 114.06 22.55 "深圳"
(integer) 3

# 2. 计算两个位置之间的距离
> GEODIST locations "北京" "上海" km
"1067.3788"

# 3. 获取位置的坐标
> GEOPOS locations "北京"
1) 1) "116.39000087976455688"
   2) "39.91000003849858164"

# 4. 获取位置的Geohash表示
> GEOHASH locations "北京"
1) "wx4g0b7xrt0"

# 5. 查找指定半径内的位置
> GEORADIUS locations 116.39 39.91 1000 km
1) "北京"
2) "上海"

# 6. 查找某个点附近的位置
> GEORADIUSBYMEMBER locations "上海" 300 km
1) "上海"
2) "杭州"

10.事务和管道操作

Redis事务保证命令的原子性执行,管道批量执行命令提高性能:

# 1. 开始事务
> MULTI
OK

# 2. 添加命令到事务队列
> SET account:balance 1000
QUEUED
> DECRBY account:balance 100
QUEUED
> SET account:lastop "withdrawal"
QUEUED

# 3. 执行事务
> EXEC
1) OK
2) (integer) 900
3) OK

# 4. 放弃事务
> MULTI
OK
> SET key1 "value1"
QUEUED
> DISCARD
OK

# 5. 监视键的变化(乐观锁)
> WATCH account:balance
OK
> MULTI
OK
> DECRBY account:balance 100
QUEUED
> EXEC  # 如果account:balance在此期间被修改,返回nil
1) (integer) 800

11.Lua脚本

Lua脚本是Redis的强大功能,可以实现原子操作和复杂逻辑:

# 1. 执行Lua脚本
> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

# 2. 原子递增并检查限制
> EVAL "local current = redis.call('INCR', KEYS[1]) if current > tonumber(ARGV[1]) then return 0 else return 1 end" 1 rate:limit:user:1001 10
(integer) 1

# 3. 加载脚本到缓存
> SCRIPT LOAD "return redis.call('GET', KEYS[1])"
"6b1bf486c81ceb7edf3c093f4c48582e38c0e791"

# 4. 使用缓存的脚本
> EVALSHA "6b1bf486c81ceb7edf3c093f4c48582e38c0e791" 1 mykey
(nil)

# 5. 检查脚本是否已缓存
> SCRIPT EXISTS "6b1bf486c81ceb7edf3c093f4c48582e38c0e791"
1) (integer) 1

# 6. 清除脚本缓存
> SCRIPT FLUSH
OK

12.发布/订阅功能

Redis的发布/订阅功能支持消息的发布和接收:

# 1. 订阅频道(在另一个客户端执行)
> SUBSCRIBE news updates
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "news"
3) (integer) 1
1) "subscribe"
2) "updates"
3) (integer) 2

# 2. 发布消息
> PUBLISH news "Breaking news: Redis 7.0 released!"
(integer) 1

# 3. 按模式订阅频道
> PSUBSCRIBE news:*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "news:*"
3) (integer) 1

# 4. 查看活跃频道
> PUBSUB CHANNELS
1) "news"
2) "updates"

# 5. 查看频道订阅数
> PUBSUB NUMSUB news
1) "news"
2) (integer) 1

13.Redis命令的最佳实践

13.1. 键名设计规范

# 推荐使用冒号分隔的命名空间
user:profile:1001    # 用户资料
user:sessions:1001   # 用户会话
product:info:10086   # 产品信息
product:stock:10086  # 产品库存

# 避免使用非常长的键名,浪费内存

13.2. 避免使用危险命令

# 生产环境禁用或重命名这些命令
KEYS *        # 可能阻塞Redis服务器
FLUSHALL      # 清空所有数据库
FLUSHDB       # 清空当前数据库
CONFIG         # 修改配置

# 在redis.conf中:
rename-command FLUSHALL ""
rename-command KEYS "KEYS_ADMIN_ONLY"

13.3. 使用SCAN代替KEYS

# 使用SCAN命令渐进式遍历键空间
> SCAN 0 MATCH user:* COUNT 100
1) "1536"
2) 1) "user:1001"
   2) "user:1002"
   3) "user:1003"

# 对应的还有HSCAN, SSCAN, ZSCAN
> HSCAN user:1001 0 COUNT 100

13.4. 高效的原子操作

# 使用SETNX实现简单的分布式锁
> SETNX lock:resource1 "process-id"
(integer) 1
# 处理完毕后释放锁
> DEL lock:resource1
(integer) 1

# 更安全的分布式锁(带过期时间)
> SET lock:resource1 "process-id" NX EX 10
OK

13.5. 使用管道提高性能

在实际应用中,使用客户端的管道功能批量执行命令可显著提高性能:

# Python示例 - 不使用管道
import redis
r = redis.Redis()
for i in range(1000):
    r.set(f"key:{i}", f"value:{i}")

# 使用管道 - 性能提升5-10倍
pipe = r.pipeline()
for i in range(1000):
    pipe.set(f"key:{i}", f"value:{i}")
pipe.execute()

14.实用命令组合技巧

14.1. 实现限流器

# 每分钟限制API调用次数
> INCR rate:limit:api:user:1001:60
(integer) 1
> EXPIRE rate:limit:api:user:1001:60 60
(integer) 1

# 或使用更简洁的方式
> SET rate:limit:api:user:1001:60 1 EX 60 NX
OK

14.2. 实现计数器

# 页面访问计数
> INCR page:views:homepage
(integer) 42

# 独立访客计数(使用HyperLogLog)
> PFADD page:uv:homepage "user1" "user2"
(integer) 1
> PFCOUNT page:uv:homepage
(integer) 2

14.3. 实现排行榜

# 添加分数
> ZADD leaderboard:daily 1000 "user:1001"
(integer) 1
> ZADD leaderboard:daily 1200 "user:1002"
(integer) 1

# 获取前3名
> ZREVRANGE leaderboard:daily 0 2 WITHSCORES
1) "user:1002"
2) "1200"
3) "user:1001"
4) "1000"

# 获取用户排名
> ZREVRANK leaderboard:daily "user:1001"
(integer) 1  # 第二名,排名从0开始

14.4. 实现会话存储

# 存储会话数据
> HMSET session:token123 user_id 1001 login_time "2023-02-09T10:30:00" ip "192.168.1.1"
OK
> EXPIRE session:token123 1800
(integer) 1

# 更新会话过期时间(延长会话)
> EXPIRE session:token123 1800
(integer) 1

14.5. 实现消息队列

# 生产者:添加任务
> LPUSH tasks "task_data_1"
(integer) 1

# 消费者:获取并处理任务
> BRPOP tasks 5
1) "tasks"
2) "task_data_1"

总结
Redis命令丰富而强大,本文精选的60个命令覆盖了Redis的核心功能,从基础操作到高级特性。通过学习这些命令,你可以更好地发挥Redis的性能优势,解决实际应用中的各种问题。

在实际应用中,建议遵循以下原则:

使用规范的键名设计
避免使用可能阻塞服务器的命令
合理利用Redis的数据类型和特性
使用管道和Lua脚本提高性能
定期监控和优化Redis使用

五、redis 数据导出 key

批量导出脚本

[root@nginx1 src]# vim export.sh 

#!/bin/bash

src_array=("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" "127.0.0.1:7006")

for src in "${src_array[@]}"
do
    OLD_IFS="$IFS"
    IFS=":"
    array=($src)
    IFS=$OLD_IFS

    src_ip=${array[0]}
    src_port=${array[1]}

    echo "$src_ip and $src_port"

    echo "keys *" | redis-cli -h $src_ip -p $src_port > keys_${src_ip}_${src_port}.txt
done

[root@nginx1 src]# chmod a+x export.sh 
[root@nginx1 src]# ./export.sh 
127.0.0.1 and 7001
127.0.0.1 and 7002
127.0.0.1 and 7003
127.0.0.1 and 7004
127.0.0.1 and 7005
127.0.0.1 and 7006

导出为txt文本

六、redis数据持久化

工作中经常会遇到 Redis 数据库相关的使用操作,因为其将数据存储在内存中的缘故,其数据的读写效率要远远高于数据库等方式的读写。但也因为数据存储在内存中,如果机器意外关机,就会导致数据的丢失。为了避免数据丢失造成的损失,因此就需要对 Redis 中的数据进行持久化的备份处理。本篇是对最近学习 Redis 数据持久化的一个笔记,简要介绍了 Redis 提供的 RDB (快照方式) 与 AOF (只追加文件) 两种持久化方式的使用。

1.RDB 持久化

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot 快照,它恢复时是将快照文件直接读到内存里,这个快照文件默认是dump.rdb ,可以在redis.conf 中查看与修改。

Redis 会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO 操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB 方式要比AOF 方式更加的高效。RDB 的缺点是最后一次持久化后的数据可能丢失。其中rdb 保存的是dump.rdb 文件。

1.1.什么是fork

fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)。数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。

1.2.配置快照

[root@nginx1 ~]# cd /wwq/packages/redis-3.2.2
[root@nginx1 redis-3.2.2]# vim redis.conf 

在redis.conf 配置文件中有关于快照的相关配置如下图:在满足下面的配置信息时将会触发RDB 。

1.3.如何触发RDB快照

  1. 执行Save:save时只管保存,其它不管,全部阻塞 。
  2. 执行BGSAVE:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave 命令获取最后一次成功执行快照的时间 。
  3. 执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义 。
  4. 自定义快照触发条件 。

1.4.数据恢复演示

//1. 更改redis.conf 配置文件中5 分钟修改10 次的触发条件为:30 秒修改1 次
save 30 1   

//2.在终端1 中删除原来的 dump.rdb文件,保证正确性
[root@localhost bin]# ls
redis-benchmark  redis-cli  redis-server

//3. 在终端2 中开启Redis 服务并做了一次设置
[root@nginx1 redis_cluster]# redis-cli -c -p 7001
127.0.0.1:7001> SET s1 v1 
-> Redirected to slot [15224] located at 127.0.0.1:7003
OK

//4. 30 秒后在终端1 中查询文件,这时就多了一个dump.rdb 文件,并将该文件复制一份
[root@localhost bin]# ls
dump.rdb  redis-benchmark  redis-cli  redis-server
[root@nginx1 src]# cp dump.rdb dump_cp.rdb
[root@localhost bin]# ls
dump_cp.rdb  redis-benchmark  redis-server
dump.rdb     redis-cli

//5.在终端2 中清除所有的数据,执行FLUSHDB ,重新启动Redis 服务,执行keys * 进行查询,发现并没有备份数据
127.0.0.1:7001> FLUSHDB 
OK
........退出,登录步骤省略
127.0.0.1:7001> keys *
(empty list or set)

//6.原因是执行FLUSHDB 也会触发快照,dump.rdb 中的数据被清空,所以我们在终端2退出Redis 服务
127.0.0.1:7001> SHUTDOWN
not connected> EXISTS

//7.在终端1 中删除dump.rdb 空文件,并将拷贝的dump_cp.rdb(改文件保存了原快照信息) 命名为 dump.rdb
mv dump_cp.rmb dump.rmb

//8.在终端2 中重新开启Redis 服务,再查询时就发现s1 数据被恢复了
[root@localhost bin]# ./redis-server /myredis/redis.conf 
[root@localhost bin]# ./redis-cli -c -p 7001
127.0.0.1:7001> keys *
1) "s1"

2.AOF 持久化

2.1什么是AOF

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis 启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。其中AOF 保存的是appendonly.aof 文件,可以在redis.conf 配置文件中查看与修改。

相关配置信息:AOF 默认配置是关闭的。

593 appendonly yes # 开启 AOF 功能,默认是 no
594 appendfilename "appendonly.aof" # 指定 aof 文件名
595 no-appendfsync-on-rewrite no #  重写时是否做 aof 操作,关闭可以节省 IO 资源消耗, 但也会造成数据丢失的可能,需要权衡
596 appendfsync always # 刷新方式

AOF (只追加文件)类似于 MySQL 的 binlog,其会将 Redis 的每一条写命令添加到 aof 文件中,本质上是先写入一个缓冲区,然后在刷新到磁盘中,可以通过 appendfsync 进行配置其刷新方式有三种:

always: 每条命令会立即刷新到 aof 文件中,这样可以保证数据不丢失,但是会导致较大的 IO 开销
everysec: 每秒刷新,可以减轻 IO 压力,但会存在丢失一秒数据的
no: 由操作系统决定何时刷新
上面三种方式,大部分都是用第二种,达到数据安全性和性能的平衡,不推荐使用第三种,虽然不需要我们进行管理,但是也不可控。对于数据安全性较高的部分数据可以考虑使用第一种 always 方式。

2.2.AOF 文件重写

AOF 的方式会将每一条写命令存到 aof 文件中,随着写命令的增多,其文件会越来越大,占用存储资源,并且恢复数据的时候也会越来越慢。为了减少资源的消耗,Redis 提供了 AOF 重写,本质就是将过期的写命令移除掉,只保留最新的一条。如下面三条命令,对 a 这个 key 我写入了三次,AOF 备份会将三条命令都写入到文件中备份,但此时其实只有第三条命令是有效的,那么 AOF 文件重写就会将 前两条命令移除掉,只保留第三条。这样可以达到减少磁盘占用和加速数据恢复的目的。

127.0.0.1:7001> set a 1
OK
127.0.0.1:7001> set a 2
OK
127.0.0.1:7001> set a 3
OK

2. 3.AOF 文件重写的两种方式

BGREWRITEAOF 命令

执行该命令,Redis 会将内存中的数据进行一次回溯,然后将对应的写入命令保存到 aof 文件中。

127.0.0.1:7001> BGREWRITEAOF
Background append only file rewriting started

查看 Redis 日志其打印如下, 可以看到重写也是先 fork 一个新的进程然后进行:

62 69318:M 02 Jun 12:17:09.042 * Background append only file rewriting started by pid 71343
63 69318:M 02 Jun 12:17:09.067 * AOF rewrite child asks to stop sending diffs.
64 71343:C 02 Jun 12:17:09.067 * Parent agreed to stop sending diffs. Finalizing AOF...
65 71343:C 02 Jun 12:17:09.068 * Concatenating 0.00 MB of AOF diff received from parent.
66 71343:C 02 Jun 12:17:09.068 * SYNC append only file rewrite performed
67 69318:M 02 Jun 12:17:09.101 * Background AOF rewrite terminated with success
68 69318:M 02 Jun 12:17:09.101 * Residual parent diff successfully flushed to the rewritten AOF (0.00 MB)
69 69318:M 02 Jun 12:17:09.102 * Background AOF rewrite finished successfully

重写自动配置

Redis 提供了两个重写相关的配置


664 auto-aof-rewrite-min-size 64mb # AOF 文件重写所需的尺寸,当 aof 文件达到该值时进行一次重写
665 auto-aof-rewrite-percentage 100 # AOF 文件的增长率, 即 AOF 变大多少时再次重写,100 表示下次到达 128 M 时进行重写

Redis 中提供了 aof_curren_size 和 aof_base_size 分别表示当前 AOF 文件大小和上次重写时的 AOF 文件大小,其自动重写就是满足下面两个条件:

aof_curren_size > auto-aof-rewrite-min-size
aof_curren_size - aof_base_size / aof_base_size > auto-aof-rewrite-percentage