Redis数据库
Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
- 没有依赖关系的清理轻量级:用ANSI C编写,没有外部依赖关系。适用于所有POSIX环境。
- 高可用性:内置支持异步,非阻塞,主从复制,以确保数据的高可用性目前有一种名为Redis Sentinel的高可用性解决方案目前可以使用,但仍被视为正在进行的工作。
-
缓存 session 会话
缓存用户信息,找不到再去 mysql 查,查到然后回写到redis
优惠卷过期时间
-
排行榜-列表&有序集合
热度排名排行榜
发布时间排行榜
-
计数器应用-天然支持计数器
帖子浏览数
视频播放次数
商品浏览数
-
社交网络-集合
踩/赞,粉丝,共同好友/喜好,推送,打标签
-
消息队列系统
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
### redis 下载目录 /data/ ### redis 安装目录 /data/redis ### redis 数据目录 /data/redis_{PORT}/redis_{PORT}.rdb ### redis 运维脚本 /root/scripts/redis_shell.sh
### 编辑 hosts 文件 [root@db01 ~]# vim /etc/hosts 10.0.0.51 db01 10.0.0.52 db02 10.0.0.53 db03 yum -y install gcc automake autoconf libtool make make distclean mkdir -p /data/ mkdir -p /data/redis_6379 cd /data/ wget http://download.redis.io/releases/redis-3.2.12.tar.gz tar -xf redis-3.2.12.tar.gz mv /data/redis-3.2.12/ /data/redis cd /data/redis make && make install ### 环境变量 vim /etc/profile ### 添加以下一行 export PATH=/data/redis/src:$PATH ### 生效配置 source /etc/profile
- 添加配置文件
cat > /data/redis_6379/redis.conf<<EOF daemonize yes bind 10.0.0.51 127.0.0.1 port 6379 logfile /data/redis_6379/redis.log pidfile /var/run/redis6379.pid databases 16 dir /data/redis_6379 dbfilename redis_6379.rdb EOF
- 连接测试
### 进入后台启动 redis-server & ### 连接测试 redis-cli 127.0.0.1:6379> set num 10 OK 127.0.0.1:6379> get num "10"
-
文件启动和关闭
注:前提将指的的配置文件创建好
### 启动 [root@db01 ~]# redis-server .data/redis_6379/redis.conf [root@db01 ~]# ps -ef | grep redis [root@db01 ~]# netstat -lntup ### 关闭 [root@db01 ~]# redis-cli shutdown
用service来管理服务的时候,是在/etc/init.d/
目录中创建一个脚本文件,来管理服务的启动和停止,在systemctl中,也类似,文件目录有所不同,在/lib/systemd/system
目录下创建一个脚本文件 redis.service,里面的内容如下:
cat > /lib/systemd/system/redis6379.service <<EOF [Unit] Description=Redis persistent key-value database After=network.target After=network-online.target Wants=network-online.target [Service] ExecStart=/usr/local/bin/redis-server /data/redis_6379/redis.conf --supervised systemd ExecStop=/usr/local/bin/redis-cli -h -p 6379 shutdown Type=forking User=redis Group=redis RuntimeDirectory=redis RuntimeDirectoryMode=0755 [Install] WantedBy=multi-user.targe EOF
- 创建用户
groupadd redis useradd redis -M -g redis -s /sbin/nologin chown -R redis.redis /data/redis_*
|
介绍 |
Type=oneshot |
这一选项适用于只执行一项任务、随后立即退出的服务。可能需要同时设置 RemainAfterExit=yes 使得 systemd 在服务进程退出之后仍然认为服务处于激活状态。 |
Type=notify |
与 Type=simple 相同,但约定服务会在就绪后向 systemd 发送一个信号。这一通知的实现由 libsystemd-daemon.so 提供。 |
Type=dbus |
若以此方式启动,当指定的 BusName 出现在DBus系统总线上时,systemd认为服务就绪。 |
Type=idle |
systemd会等待所有任务处理完成后,才开始执行 idle 类型的单元。其他行为与 Type=simple 类似。 |
Type=forking |
systemd认为当该服务进程fork,且父进程退出后服务启动成功。对于常规的守护进程(daemon),除非你确定此启动方式无法满足需求,使用此类型启动即可。使用此启动类型应同时指定 PIDFile=,以便 systemd 能够跟踪服务的主进程 |
Type=simple |
|
- 创建软连接
注:设置开机自启
ln -s /lib/systemd/system/redis6379.service /etc/systemd/system/multi-user.target.wants/redis6379.service
- 开机启动
创建完redis.service后 systemctl daemon-reload --更新systemctl systemctl start redis systemctl restart redis systemctl stop redis ##systemctl 进入redis redis-cli -p 6379
### 以守护进程模式开启 daemonize yes ### 绑定的主机地址 bind 10.0.0.51 ### 监听端口 port 6379 ### pid文件和log文件的保存地址 pidfile /opt/redis_6379/pid/redis_6379.pid logfile /opt/redis_6379/log/redis_6379.log ### 设置数据库的数量,m默认数据库为0 database 16 ### 指定本地持久文件的文件名,默认是dump.rdb dbfilenam dump.rdb ### 本地数据库的目录 dir /data/redis_6379
set k1 v1
set k2 v2
set k3 v3
127.0.0.1:6379> keys * 1) "k3" 2) "k2" 3) "k1"
127.0.0.1:6379> DBSIZE (integer) 3
注:返回1,存在估值,返回 0 则不存在
127.0.0.1:6379> EXISTS k1 (integer) 1 127.0.0.1:6379> EXISTS k1 k2 k3 (integer) 3 127.0.0.1:6379> EXISTS k1 k2 k4 (integer) 2 #这里实际就创建了两个key值,没有k4,所以显示2
状态码:
-
-
1:表示这个key存在
-
127.0.0.1:6379> DEL k1 (integer) 1 127.0.0.1:6379> DEL k2 k3 (integer) 2
状态码:
-
0:表示这个key不存在
-
1:表示这个key存在,并且删除成功
-
# 设置过期时间 127.0.0.1:6379> EXPIRE k1 100 (integer) 1 # 取消过期时间 127.0.0.1:6379> PERSIST K1 (integer) 1
状态码:
-
0:表示这个key不存在
-
1:表示这个key存在,并且设置过期之间成功
127.0.0.1:6379> TTL k1 (integer) 96 127.0.0.1:6379> TTL k1 (integer) 95 127.0.0.1:6379> TTL k1 (integer) 94 127.0.0.1:6379> TTL k1 (integer) 93
注:过期后的key
直接删除
状态码:
-
-1:这个key存在,并且永不过期
-
-2:这个key不存在
-
127.0.0.1:6379> set k1 v1 OK
2.查看一个key
127.0.0.1:6379> get k1 "v1"
3.设置多个key
127.0.0.1:6379> MSET k1 v1 k2 v2 k3 v3 OK
4.查看多个key
127.0.0.1:6379> MGET k1 k2 k3 1) "v1" 2) "v2" 3) "v3"
5.天然计数器
#加1: 127.0.0.1:6379> set k1 1 ##进行+1操作 127.0.0.1:6379> INCR k1 (integer) 2 127.0.0.1:6379> INCR k1 (integer) 3 127.0.0.1:6379> INCR k1 (integer) 4 #查看 127.0.0.1:6379> get k1 "4" #加N: 127.0.0.1:6379> INCRBY k1 100 (integer) 104 #减1: 127.0.0.1:6379> INCRBY k1 -1 (integer) 103 减N: INCRBY K1 -N
## LPUSH:从列表左侧插入数据 ## RPUSH:从列表右侧插入数据 127.0.0.1:6379> LPUSH list1 A 127.0.0.1:6379> LPUSH list1 B 127.0.0.1:6379> LPUSH list1 C 127.0.0.1:6379> RPUSH list1 D ## 查看列表的长度: 127.0.0.1:6379> LLEN list1 (integer) 4 ## 查看列表的内容: 127.0.0.1:6379> LRANGE list1 0 -1 1) "C" 2) "B" 3) "A" 4) "D" ## 删除列表元素 LPOP:从列表左侧删除 EPOP:从列表右侧删除 127.0.0.1:6379> LPOP list1 "C" 127.0.0.1:6379> RPOP list1 "D" ## 删除列表内容 127.0.0.1:6379> DEL list1 (integer) 1
Redis hash 是一个 string 类型的field 和 value 的映射表,hash 特别适合用于存储对象。
Redis 中每个 hash 可以存储40多亿键值对。
哈希:
-
Hash
看起来就像一个'hash'的样子.由键值对组成. -
HMSET
指令设置hash中的多个域. -
HGET
取回单个域. -
## 生成一个hash类型 127.0.0.1:6379> HMSET user:1 name xiaozhang job it age 28 127.0.0.1:6379> MHSET user:2 name abc job it age 18 127.0.0.1:6379> MHSET user:3 name def job it age 19 ## 查看hash里的多个值 127.0.0.1:6379> MHGET user:1 name age job 1) "xiaozhang" 2) "28" 3) "it" ## 查看hash里的所有的值 127.0.0.1:6379> HGETALL user:1 1) "name" 2) "xiaozhang" 3) "job" 4) "it" 5) "age" 6) "28"
- mysql数据和redis哈希对比:
user表: uid name job age 1 xiaozhang it 28 2 xiaoya it 28 3 yazhang it 28 mysql查询数据 select * from user where id=3 redis缓存mysql数据 名字 key1 k1值 key2 k2值 key3 k3值 uid:1 name xiaozhang job it age 28 uid:2 name xiaoli job it age 28 uid:3 name xiaotian job it age 28
集合通俗来讲就像qq上的共同好友
## 创建集合 127.0.0.1:6379> SADD set1 1 2 3 (integer) 3 127.0.0.1:6379> SADD set2 1 3 5 7 (integer) 5 ## 查看集合的成员: 127.0.0.1:6379> SMEMBERS set1 1) "1" 2) "2" 3) "3" 127.0.0.1:6379> SMEMBERS set2 1) "1" 2) "3" 3) "5" 4) "7" ## 查看集合的差集,以前面一个差集为基准对比后面的,前面有,后面没有则选出来 127.0.0.1:6379> SDIFF set1 set2 1) "2" 127.0.0.1:6379> SDIFF set2 set1 1) "5" 2) "7" ## 查看集合的交集 127.0.0.1:6379> SINTER set1 set2 1) "1" 2) "3" ## 查看集合的并集 127.0.0.1:6379> SUNION set1 set2 1) "1" 2) "2" 3) "3" 4) "5" 5) "7"
注:集合不允许出现重复的值
Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。
有序集合的成员是唯一的,但分数(score)却可以重复。
应用场景:排行榜应用
- 国家排名例子:
127.0.0.1:6379> ZADD country 0 china 0 usa 0 japan 0 uk (integer) 4 127.0.0.1:6379> ZINCRBY country 10000 china "10000" 127.0.0.1:6379> ZINCRBY country 100 usa "100" 127.0.0.1:6379> ZINCRBY conurty 1000 japan "1000" 127.0.0.1:6379> ZINCRBY country 1000000 uk "1000000" 127.0.0.1:6379> ZREVRANGE country 0 -1 1) "uk" 2) "china" 3) "usa" 4) "japan"
消息模式是为了帮助解决在架构中,资源有效利用方面提供有效的协调。
pulisher 发布者
channel 频道
subscriber 订阅者
命令:
命令 | 解释 |
PUBLISH channel msg | 将信息 message 发送到指定的频道 channel |
SUBSCRIBE channel [channel ...] | 订阅频道,可以同时订阅多个频道 |
UNSUBSCRIBE [channel ...] | 取消订阅指定的频道, 如果不指定频道,则会取消订阅所有频道 |
PSUBSCRIBE pattern [pattern ...] | 订阅一个或多个符合给定模式的频道,每个模式以 * 作为匹配符,比如 it* 匹配所有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), news.* 匹配所有 以 news. 开头的频道( news.it 、 news.global.today 等等),诸如此类 |
PUNSUBSCRIBE [pattern [pattern ...]] | 退订指定的规则, 如果没有参数则会退订所有规则 |
PUBSUB subcommand [argument [argument ...]] |
发布订阅的例子:
## 窗口一: 127.0.0.1:6379> SUBSCRIBE taobao ## 窗口二: 127.0.0.1:6379> PUBLISH taobao "hello word"
订阅多频道:
## 窗口一: 127.0.0.1:6379> PSUBSCRINE tianmao* ## 窗口二: 127.0.0.1:6379> PUBLISH tian* "hello china"
redis事务可以一次执行多个命令,并且带有一下三个重要保证:
-
批量操作在发送
EXEC
命令前被放入队列缓存。 -
收到
EXEC
命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。 -
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历一下三个阶段:
-
-
命令入列。
-
redis事务和MySQL事务的区别:
底层原理图:
注:命令相同,同时分两个消息队列,谁先执行EXEC
就算谁的。
- 开启事务功能时(multi)
127.0.0.1:6379> multi OK 127.0.0.1:6379> set a 1 QUEUED 127.0.0.1:6379> set b 2 QUEUED 127.0.0.1:6379> exec 1) OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> set x 10 QUEUED 127.0.0.1:6379> set y 20 QUEUED 127.0.0.1:6379> DISCARD OK
-
redis乐观锁实现
session 1: watch ticket multi decr ticket exec session 2: multi decr ticket exec
-
可以在指定的时间间隔内生成数据集的 时间点快照(point-in-time snapshot),新快照会覆盖老快照。
优缺点:恢复速度快。适合用于做备份,主从复制基于RDB持久化功能实现。
缺点:可能会丢失数据
-
AOF持久化(append-only log file):
类似于MySQL的binlog,重写,1秒写一次或者每次操作都记录
优点:安全,有可能会丢失1秒数据
vim /data/redis_6379/redis.conf save 900 1 save 300 10 save 60 10000 dir /data/redis_6379/ dbfilename redis_6379.rdb 配置分别表示: 900秒(15分钟)内有1个更改 300秒(5分钟)内有10个更改 60秒内有10000个更改
结论:
-
执行shutdown的时候,内部会自动执行bgsave,然后在执行shutdown
-
pkill
、kill
、killall
都类似于执行shutdown
命令,会触发bgsave持久化 -
恢复的时候,rdb文件名称要和配置文件里写的一样
-
-
- 常用命令:
ll /data/redis_6379/ cat /opt/redis_6379/conf/redis_6379.conf vim /opt/redis_6379/conf/redis_6379.conf pkill redis redis-server /data/redis_6379/redis.conf redis-cli -h db01 redis-cli -h db01 shutdown bash for.sh
appendonly yes appendfsync always appendfsync everysec appendfsync no vim /data/6379/redis.conf appendfilename "redis_6379.aof" appendonly yes appendfsync everysec
-
面试:
redis 持久化方式有哪些?有什么区别?
-
rdb:基于快照的持久化,速度更快,一般用作备份,主从复制依赖于rdb持久化功能。
-
save bgsave区别?
共同点:都能实现RDB持久化功能
不同点:
-
SAVE:前台,阻塞redis正常写入,直到持久化完成。
-
BGSAVE:后台,开启子线程,异步的持久化功能,不会阻塞redis正常写入
-
实验:
如果aof和rdb文件同时存在,redis会如何读取:
实验步骤:
-
插入一条数据
aof:有记录
rdb:没有记录
-
复制到其他地方
-
把redis停掉
-
清空数据目录
-
把数据文件拷贝过来
aof:有记录
rdb:没有记录
-
启动redis
-
测试,如果有新插入的数据,就表示读取的是aof,如果没有表示读取的是rdb
实验结论:如果2中数据格式都存在,优先读取aof
-
副本库通过slaveof 10.0.0.51 6379命令,连接主库,并发送SYNC给主库
-
主库收到SYNC,会立即触发BGSAVE,后台保存RDB,发送给副本库
-
副本库接受后会应用RDB快照
-
主库会陆续将中间产生的新的操作,保存并发送给副本库
-
到此,我们主从复制集就正常工作了
-
在此以后,主库只要发生新的操作,都会以命令传播的形式自动发送给副本库
-
所有复制相关信息,从info信息中都可以查到。即使重启任何节点,它的主从关系都在。
-
2.8+ 版本的PSYNC功能
-
如果发生主从关系断开时,主从数据没有任何损失,在下次重连之后,从库发送SYNC给主库
-
主库只会将从库缺失的数据同步给从库应用,达到快速恢复主库的目的
原理图:
- 主从数据一致性保证
min-slaves-to-write 1 min-slaves-max-lag 3 ---三秒内证明从库还活着
-
主库是否要开启持久化
节点 | port | 数据目录 |
主节点 6380 | 6380 | /data/redis_6380 |
从节点 6381 | 6381 | /data/redis_6381 |
从节点 6382 | 6382 |
1.准备实例
mkdir /data/redis_638{0..2} #配置文件示例: cat >> /data/redis_6380/redis.conf <<EOF port 6380 daemonize yes pidfile /data/redis_6380/redis.pid loglevel notice logfile "/data/redis_6380/redis.log" dbfilename dump.rdb dir /data/redis_6380 requirepass 123 masterauth 123 EOF cat >> /data/redis_6381/redis.conf <<EOF port 6381 daemonize yes pidfile /data/redis_6381/redis.pid loglevel notice logfile "/data/redis_6381/redis.log" dbfilename dump.rdb dir /data/redis_6381 requirepass 123 masterauth 123 EOF cat >> /data/redis_6382/redis.conf <<EOF port 6382 daemonize yes pidfile /data/redis_6382/redis.pid loglevel notice logfile "/data/redis_6382/redis.log" dbfilename dump.rdb dir /data/redis_6382 requirepass 123 masterauth 123 EOF
- 启动
redis-server /data/redis_6380/redis.conf redis-server /data/redis_6381/redis.conf redis-server /data/redis_6382/redis.conf
2.开启主从
6381/6382命令行
redis-cli -p 6381 -a 123 SLAVEOF 127.0.0.1 6380 redis-cli -p 6382 -a 123 SLAVEOF 127.0.0.1 6380
3.查询主从状态
redis-cli -p 6380 -a 123 info replication redis-cli -p 6381 -a 123 info replication redis-cli -p 6382 -a 123 info replication
4.解除主从
[root@db01 ~]# redis-cli -p 6381 -a 123 SLAVEOF no one
哨兵的作用:
-
监控redis集群
-
自动选主,切换(6381 slaveof no one)
-
2号库(6382)指向新主库(6381)
-
应用透明
-
自动处理故障节点
- sentinel搭建过程
## 创建目录 mkdir /data/redis_26380 cat > /data/redis_26380/sentinel.conf<<EOF port 26380 dir "/data/redis_26380" sentinel monitor mymaster 127.0.0.1 6380 1 sentinel down-after-milliseconds mymaster 5000 sentinel auth-pass mymaster 123 EOF
配置文件参数说明:
启动: [root@db01 26380]# redis-sentinel /data/redis_26380/sentinel.conf &>/tmp/sentinel.log & 关闭命令: [root@db01 26380]# redis-cli -p 26380 shutdown
如果出现问题:
-
重新准备1主2从环境
-
-
删除sentinel目录下的所有文件
-
- 停库主测试:
## 窗口一: [root@db01 ~]# redis-cli -p 6380 shutdown [root@db01 ~]# redis-cli -p 6381 -a 123 info replication --查看主节点现在是谁? [root@db01 ~]# redis-cli -p 6381 info replication ## 窗口二: [root@db01 ~]# tail -f /tmp/sentinel.log 10237:X 14 Mar 22:19:29.318 # +promoted-slave slave 127.0.0.1:6382 127.0.0.1 6382 @ mymaster 127.0.0.1 6380 10237:X 14 Mar 22:19:29.318 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6380 10237:X 14 Mar 22:19:29.319 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380 10237:X 14 Mar 22:19:30.378 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380 10237:X 14 Mar 22:19:30.379 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380 10237:X 14 Mar 22:19:30.454 # +failover-end master mymaster 127.0.0.1 6380 ## 故障转移端是127.0.0.1 6380实例 10237:X 14 Mar 22:19:30.455 # +switch-master mymaster 127.0.0.1 6380 127.0.0.1 6382 ## 主节点切换到了6382实例上面 10237:X 14 Mar 22:19:30.455 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6382 10237:X 14 Mar 22:19:30.455 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6382 10237:X 14 Mar 22:19:35.528 # +sdown slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6382 10237:X 14 Mar 22:22:58.321 # -sdown slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6382 ## 让启动好的6380节点接入6382为主节点 10237:X 14 Mar 22:23:08.242 * +convert-to-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6382
- Sentinel 管理命令:
redis-cli -p 26380 PING :返回 PONG 。 SENTINEL masters :列出所有被监视的主服务器 SENTINEL slaves <master name> SENTINEL get-master-addr-by-name <master name> : 返回给定名字的主服务器的 IP 地址和端口号。 SENTINEL reset <pattern> : 重置所有名字和给定模式 pattern 相匹配的主服务器。 SENTINEL failover <master name> : 当主服务器失效时, 在不询问其他 Sentinel 意见的情况下, 强制开始一次自动故障迁移。
注:哨兵一般有三个,这可以根据上面在的配置内容在创建两个,选票数则修改为2
-
在多分片节点中,将16384个槽位,均匀分布到多个分片节点中
-
存数据时,将key做crc16(key),然后和16384进行取模,得出槽位(0-16383)
-
根据计算得出的槽位值,找到想对应的分片节点的主节点,存储到相应槽位上
-
如果客户端当时连接的节点不是将来要存储的分片节点,分片集群会将客户端连接却换至真正存储节点进行数据存储
高可用:
在搭建集群时,会为每一个分片的主节点,对应一个从节点,实现slaveof的功能,同时当主节点down,实现类似于sentinel的自动fileover的功能。
-
redis会为多组分片构成(3组)
-
redis cluster 使用固定个数的slot存储数据(一个16384slot)
-
每组分片分得1/3 slot个数(0-5500 5501-11000 11001-16383)
-
基于CRC16(key)% 16384 --> 值 (槽位号)
注:在企业规划中,一个分片的两个分到不同的物理服务器,防止硬件主机宕机造成的整个分片数据丢失。
原理图:
1.安装集群插件
EPEL源安装ruby支持 yum install ruby rubygems -y
2.配置gem源,并安装驱动
## gem安装需要repo源 curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo gem sources -l gem sources -a http://mirrors.aliyun.com/rubygems/ gem sources --remove https://rubygems.org/ gem sources -l gem install redis -v 3.3.3
3.集群节点准备
mkdir /data/redis_700{0..5} cat > /data/redis_7000/redis.conf <<EOF port 7000 daemonize yes pidfile /data/redis_7000/redis.pid loglevel notice logfile "/data/redis_7000/redis.log" dbfilename dump.rdb dir /data/redis_7000 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes EOF cat >> /data/redis_7001/redis.conf <<EOF port 7001 daemonize yes pidfile /data/redis_7001/redis.pid loglevel notice logfile "/data/redis_7001/redis.log" dbfilename dump.rdb dir /data/redis_7001 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes EOF cat >> /data/redis_7002/redis.conf <<EOF port 7002 daemonize yes pidfile /data/redis_7002/redis.pid loglevel notice logfile "/data/redis_7002/redis.log" dbfilename dump.rdb dir /data/redis_7002 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes EOF cat >> /data/redis_7003/redis.conf <<EOF port 7003 daemonize yes pidfile /data/redis_7003/redis.pid loglevel notice logfile "/data/redis_7003/redis.log" dbfilename dump.rdb dir /data/redis_7003 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes EOF cat >> /data/redis_7004/redis.conf <<EOF port 7004 daemonize yes pidfile /data/redis_7004/redis.pid loglevel notice logfile "/data/redis_7004/redis.log" dbfilename dump.rdb dir /data/redis_7004 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes EOF cat >> /data/redis_7005/redis.conf <<EOF port 7005 daemonize yes pidfile /data/redis_7005/redis.pid loglevel notice logfile "/data/redis_7005/redis.log" dbfilename dump.rdb dir /data/redis_7005 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes EOF ## 启动节点: redis-server /data/redis_7000/redis.conf redis-server /data/redis_7001/redis.conf redis-server /data/redis_7002/redis.conf redis-server /data/redis_7003/redis.conf redis-server /data/redis_7004/redis.conf redis-server /data/redis_7005/redis.conf [root@db01 ~]# ps -ef |grep redis root 8854 1 0 03:56 ? 00:00:00 redis-server *:7000 [cluster] root 8858 1 0 03:56 ? 00:00:00 redis-server *:7001 [cluster] root 8860 1 0 03:56 ? 00:00:00 redis-server *:7002 [cluster] root 8864 1 0 03:56 ? 00:00:00 redis-server *:7003 [cluster] root 8866 1 0 03:56 ? 00:00:00 redis-server *:7004 [cluster] root 8874 1 0 03:56 ? 00:00:00 redis-server *:7005 [cluster]
4.将节点加入集群管理
利用ruby脚本 创建 副本为1.
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 ... 敲yes
5.查看集群状态
集群主节点状态 redis-cli -p 7000 cluster nodes | grep master 集群从节点状态 redis-cli -p 7000 cluster nodes | grep slave
## 创建目录 mkdir /data/redis_700{6..7} ## 配置文件信息 cat > /data/redis_7006/redis.conf <<EOF port 7006 daemonize yes pidfile /data/redis_7006/redis.pid loglevel notice logfile "/data/redis_7006/redis.log" dbfilename dump.rdb dir /data/redis_7006 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes EOF cat > /data/redis_7007/redis.conf <<EOF port 7007 daemonize yes pidfile /data/redis_7007/redis.pid loglevel notice logfile "/data/redis_7007/redis.log" dbfilename dump.rdb dir /data/redis_7007 protected-mode no cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes EOF ## 启动服务节点 redis-server /data/redis_7006/redis.conf redis-server /data/redis_7007/redis.conf netstat -lntup | grep 700*
2.添加主节点
redis-trib.rb add-node 127.0.0.1:7006 127.0.0.1:7000
3.转移slot(重新分片)
[root@db01 ~]# redis-trib.rb reshard 127.0.0.1:7000 >>> Performing Cluster Check (using node 127.0.0.1:7000) M: b64016c2d42836ed5bbf16115e1785957b552805 127.0.0.1:7000 slots:1365-5460 (4096 slots) master 1 additional replica(s) M: 881a713dcd1569a2426a69038f76e9718884e227 127.0.0.1:7006 slots:0-1364,5461-6826,10923-12287 (4096 slots) master 0 additional replica(s) M: 50d0e7523be1a7459d49d4a2a6880682d4943679 127.0.0.1:7002 slots:12288-16383 (4096 slots) master 1 additional replica(s) S: 254e2e852db3c2123e76d83bedda87f9dc3b02a6 127.0.0.1:7004 slots: (0 slots) slave replicates 273b098710b782d485f3bca4e0613478dc04cb5b S: d0126c22cc22bad1e4120c08916184af55682ea2 127.0.0.1:7005 slots: (0 slots) slave replicates 50d0e7523be1a7459d49d4a2a6880682d4943679 S: 8d2ccb6fb81f5dfb101fe7aa84ed19513626effe 127.0.0.1:7003 slots: (0 slots) slave replicates b64016c2d42836ed5bbf16115e1785957b552805 M: 273b098710b782d485f3bca4e0613478dc04cb5b 127.0.0.1:7001 slots:6827-10922 (4096 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. How many slots do you want to move (from 1 to 16384)? 4096 What is the receiving node ID? 881a713dcd1569a2426a69038f76e9718884e227 Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs. Source node #1:all Do you want to proceed with the proposed reshard plan (yes/no)? yes
4.添加一个从节点
## 查看节点 [root@db01 ~]# redis-cli -p 7000 cluster nodes | grep master 881a713dcd1569a2426a69038f76e9718884e227 127.0.0.1:7006 master - 0 1584256924836 7 connected 0-1364 5461-6826 10923-12287 50d0e7523be1a7459d49d4a2a6880682d4943679 127.0.0.1:7002 master - 0 1584256925341 3 connected 12288-16383 b64016c2d42836ed5bbf16115e1785957b552805 127.0.0.1:7000 myself,master - 0 0 1 connected 1365-5460 273b098710b782d485f3bca4e0613478dc04cb5b 127.0.0.1:7001 master - 0 1584256925846 2 connected 6827-10922 ## 添加从节点,查看自己的id号 redis-trib.rb add-node --slave --master-id 881a713dcd1569a2426a69038f76e9718884e227 127.0.0.1:7007 127.0.0.1:7000
将7006上的slot都给移走,需要分三次分别移给7000
、7001
、7002
## 查看master节点 redis-cli -p 7000 cluster nodes | grep master redis-trib.rb reshard 127.0.0.1:7000
- 第二次还原slot给7001节点
- 第三次还原slot给7002节点
注:这里一定要查好slot号,否则会留下几个删除不了节点(这里留了一个😭)
解决办法:
每个分片都是1365个,0也算,重新给slot分片,比以前多了一个,然后把分片给了那三个节点
2.删除节点
删除master节点之前首先要使用reshard移除master的全部slot,然后在删除当前节点
redis-trib.rb del-node 127.0.0.1:7006 881a713dcd1569a2426a69038f76e9718884e227 >>> Removing node 881a713dcd1569a2426a69038f76e9718884e227 from cluster 127.0.0.1:7006 >>> Sending CLUSTER FORGET messages to the cluster... >>> SHUTDOWN the node. redis-trib.rb del-node 127.0.0.1:7007 efad54782609d6bcae5a7635399c91f08cb75a0a >>> Removing node efad54782609d6bcae5a7635399c91f08cb75a0a from cluster 127.0.0.1:7007 >>> Sending CLUSTER FORGET messages to the cluster... >>> SHUTDOWN the node.
yum install -y python36 python3 -V yum install -y python36-pip pip3 install redis pip3 install redis-py-cluster
python3 >>>import redis >>>r = redis.StrictRedis(host='10.0.0.51', port=6379, db=0,password='123') >>>r.set('oldboy', 'oldguo') >>>r.get('oldboy')
[root@db01 ~]# redis-server /data/redis_6380/redis.conf [root@db01 ~]# redis-server /data/redis_6381/redis.conf [root@db01 ~]# redis-server /data/redis_6382/redis.conf [root@db01 ~]# redis-sentinel /data/redis_26380/sentinel.conf & -------------------------------- ## 导入redis sentinel包 >>>from redis.sentinel import Sentinel ##指定sentinel的地址和端口号 >>> sentinel = Sentinel([('localhost', 26380)], socket_timeout=0.1) ##测试,获取以下主库和从库的信息 >>> sentinel.discover_master('mymaster') >>> sentinel.discover_slaves('mymaster') 配置读写分离 #写节点 >>> master = sentinel.master_for('mymaster', socket_timeout=0.1,password="123") #读节点 >>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1,password="123") ###读写分离测试 key >>> master.set('oldboy','123') >>> slave.get('oldboy')
>>> from rediscluster import RedisCluster >>> startup_nodes = [{"host":"127.0.0.1", "port": "7000"},{"host":"127.0.0.1", "port": "7001"},{"host":"127.0.0.1", "port": "7002"}] ### Note: decode_responses must be set to True when used with python3 >>> rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True) >>> rc.set("foo", "bar") >>> print(rc.get("foo"))
缓存穿透
-
概念
访问一个不存在的key,缓存不起作用,请求会穿透到DB,流量大时DB会挂掉。
-
解决方案
采用布隆过滤器,使用一个足够大的bitmap,用于存储可能访问的key,不存在的key直接被过滤; 访问key未在DB查询到值,也将空值写进缓存,但可以设置较短过期时间。
缓存雪崩
-
概念
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
-
解决方案
可以给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效
缓存击穿
-
概念
一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。
-
解决方案
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。