01-秒杀案例解决库存遗留问题(掌握)
-
概述
ab -n 1000 -c 200 -p /usr/local/data.txt -T "application/x-www-form-urlencoded" 192.168.12.62:8080/doSeckill
- ab -n 1000 : 有1000个人在秒杀商品
- ab -n 1000 -c 200 : 有1000个人在秒杀商品,允许200个人同时抢1件商品,意味着虽然有1000个人在秒杀,但是只能卖出5件商品.这就是所谓的库存遗留问题.
-
解决方案
- 将并发请求变成一个请求,使用LUA脚本.
-
LUA脚本
- 将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接redis的次数。 提升性能。
- LUA脚本是类似redis事务,有一定的原子性,不会被其他命令插队,可以完成一些redis事务性的操 作。
-
代码实现
public class SeckillLUAUtils { //将之前的多次请求,使用LUA脚本拼接成一次请求 private static String secKillScript = "local userid=KEYS[1];\r\n" + "local prodid=KEYS[2];\r\n" + "local qtkey='seckill:'..prodid..\":kc\";\r\n" + "local usersKey='seckill:'..prodid..\":user\";\r\n" + "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" + "if tonumber(userExists)==1 then \r\n" + " return 2;\r\n" + "end\r\n" + "local num= redis.call(\"get\" ,qtkey);\r\n" + "if tonumber(num)<=0 then \r\n" + " return 0;\r\n" + "else \r\n" + " redis.call(\"decr\",qtkey);\r\n" + " redis.call(\"sadd\",usersKey,userid);\r\n" + "end\r\n" + "return 1"; public static boolean doSeckill(String uid, String prodid) throws IOException { Jedis jedis = JedisUtils.getJedis(); String sha1 = jedis.scriptLoad(secKillScript); Object result = jedis.evalsha(sha1, 2, uid, prodid); String reString = String.valueOf(result); if ("0".equals(reString)) { System.out.println("失败!秒杀已经结束!"); return false; } else if ("1".equals(reString)) { System.out.println("抢购成功!!!!"); } else if ("2".equals(reString)) { System.out.println("失败!您已经秒杀成功过了!把机会给别人吧!"); return false; } else { System.err.println("抢购异常!!"); return false; } jedis.close(); return true; } }
02-主从复制概述(掌握)
- 概述
- 现象1:硬盘故障、系统崩溃 ,
- 现象2:内存不足,从16G升级到64G,从64G升级到128G,无限升级内存
- 会放弃使用redis
- 解决方案
- master 数据提供方、写数据 主服务器、主节点、主库
- slave 数据接收方、读数据 从服务器、从节点、从库
- 主从复制
- 主从复制就是将master中的数据及时地、有效地复制到slave中
- 一个master可以拥有多个slave,一个slave只对应一个master
- 好处
- 读写分离
- master写、slave读,提高服务器的读写负载能力
- 负载均衡
- 基于主从结构,配合读写分离,由slave分担master负载,并根据需求的变化,改变slave的数 量,通过多个从节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量
- 故障恢复
- 当master出现问题时,由slave提供服务,实现快速的故障恢复
- 数据冗余
- 实现数据热备份,是持久化之外的一种数据冗余方式
- 高可用基石
- 基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案
- 读写分离
03-主从复制搭建(掌握)
-
需求
- 搭建1主1从的主从结构
-
master主机
port 6379 daemonize no dir "/usr/local/redis-6379/data" # 设置RDB文件 dbfilename "dump-6379.rdb" # 开启aof appendonly yes appendfilename "appendonly-6379.aof" appendfsync everysec # 设置无密码访问 protected-mode no
-
slave从机
port 26379 daemonize no dir "/usr/local/redis-26379/data" # 设置RDB文件 dbfilename "dump-26379.rdb" # 开启aof appendonly yes appendfilename "appendonly-26379.aof" appendfsync everysec # 设置无密码访问 protected-mode no slaveof 192.168.148.110 6379
-
执行流程
- ①建立链接阶段
- ②数据同步阶段
- ③命令传播阶段
04-主从复制之建立链接阶段(掌握)
- 建立连接
05-主从复制之数据同步阶段(掌握)
- 数据同步
06-部分复制的三个核心要素(掌握)
- 三个核心要素
- ①服务器运行id(run id)
- ②主服务器的复制缓冲区
- ③主从服务器的复制偏移量
- ①服务器运行id(run id)
- 服务器运行id是每一台服务器每次运行的身份识别码,一台服务器多次运行可以生成多个运行id,用于在 服务器之间进行传输识别身份
- ②主服务器的复制缓冲区
- ③主从服务器的复制偏移量
- master复制偏移量:记录发送给所有slave的指令字节对应的位置(多个)
- slave复制偏移量:记录slave接收master发送过来的指令字节对应的位置(一个
07-主从复制之命令传播阶段(掌握)
- 概述
- 当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致 的状态,同步的动作称为命令传播
- master将接收到数据变更命令发送给slave,slave接收命令后执行
- 断网现象
- 网络闪断闪连,可以忽略
- 短时间网络中断,部分复制
- 长时间网络中断,全量复制
08-哨兵模式概述(掌握)
- 在主从复制中,如果master宕机了,怎么办?
- 关闭master和所有slave
- 选举一个slave作为master
- 修改其他slave配置,连接新的master,启动新的master与slave
- 存在问题
- 关闭期间的数据服务谁来负责?
- 怎么选举?
- 原来的master恢复了怎么办?
- 哨兵模式
- sentinel是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制 选择新的master并将所有slave连接到新的master。
- 作用
- 监控:监控master和slave
- 通知(提醒)
- 自动故障转移
09-哨兵模式配置(掌握)
-
需求
- 1主2从3哨兵
-
哨兵语法
# 设置哨兵监听的主服务器信息, # sentinel_number表示参与投票的哨兵数量,哨兵总数量的一半加1 sentinel monitor <master_name> <master_host> <master_port> <sentinel_number> # 设置判定服务器宕机时长,该设置控制是否进行主从切换,单位:毫秒 sentinel down-after-milliseconds <master_name> <million_seconds> # 设置故障切换的最大超时时间,单位:毫秒 sentinel failover-timeout <master_name> <million_seconds> # 设置主从切换后,同时进行数据同步的slave数量,数值越大,要求网络资源越高,同步时间越长 sentinel parallel-syncs <master_name> <sync_slave_number>
-
开发步骤
- ①配置1个主机
- redis-6379
- ②配置2个从机
- redis-26379 , redis-26380
- ③配置3个哨兵
- redis-36379 , redis-36380 , redis-36381
- ①配置1个主机
-
①配置1个主机
port 6379 daemonize no dir "/usr/local/redis-6379/data" # 设置RDB文件 dbfilename "dump-6379.rdb" # 开启aof appendonly yes appendfilename "appendonly-6379.aof" appendfsync everysec # 设置无密码访问 protected-mode no
-
②配置2个从机
port 26379 daemonize no dir "/usr/local/redis-26379/data" # 设置RDB文件 dbfilename "dump-26379.rdb" # 开启aof appendonly yes appendfilename "appendonly-26379.aof" appendfsync everysec # 设置无密码访问 protected-mode no slaveof 192.168.148.110 6379
port 26380 daemonize no dir "/usr/local/redis-26380/data" # 设置RDB文件 dbfilename "dump-26380.rdb" # 开启aof appendonly yes appendfilename "appendonly-26380.aof" appendfsync everysec # 设置无密码访问 protected-mode no slaveof 192.168.148.110 6379
-
③配置3个哨兵
port 36379 daemonize no dir /usr/local/redis-36379/data # 设置哨兵监控的master信息:host、port、参与投票的哨兵数量 sentinel monitor mymaster 192.168.148.110 6379 2 # master宕机5秒后,进行主从切换 sentinel down-after-milliseconds mymaster 5000 # master宕机3分钟后,如果主从切换还没成功,那么就彻底失败 sentinel failover-timeout mymaster 180000 # 主从切换后,数据同步的从机数量为1 sentinel parallel-syncs mymaster 1 # 主从切换过程中,如果有问题发生,那么就终止切换 sentinel deny-scripts-reconfig yes
port 36380 daemonize no dir /usr/local/redis-36380/data # 设置哨兵监控的master信息:host、port、参与投票的哨兵数量 sentinel monitor mymaster 192.168.148.110 6379 2 # master宕机5秒后,进行主从切换 sentinel down-after-milliseconds mymaster 5000 # master宕机3分钟后,如果主从切换还没成功,那么就彻底失败 sentinel failover-timeout mymaster 180000 # 主从切换后,数据同步的从机数量为1 sentinel parallel-syncs mymaster 1 # 主从切换过程中,如果有问题发生,那么就终止切换 sentinel deny-scripts-reconfig yes
port 36381 daemonize no dir /usr/local/redis-36381/data # 设置哨兵监控的master信息:host、port、参与投票的哨兵数量 sentinel monitor mymaster 192.168.148.110 6379 2 # master宕机5秒后,进行主从切换 sentinel down-after-milliseconds mymaster 5000 # master宕机3分钟后,如果主从切换还没成功,那么就彻底失败 sentinel failover-timeout mymaster 180000 # 主从切换后,数据同步的从机数量为1 sentinel parallel-syncs mymaster 1 # 主从切换过程中,如果有问题发生,那么就终止切换 sentinel deny-scripts-reconfig yes
10-哨兵模式演示(掌握)
- 开发步骤
- ①启动1个主机
- redis-6379
- ②启动2个从机
- redis-26379 , redis-26380
- ③启动3个哨兵
- redis-36379 , redis-36380 , redis-36381
- ④查看哨兵信息
- 在主机的客户端查看
- 在从机的客户端查看
- 在哨兵的客户端查看
- ⑤演示主从切换
- 关闭redis-6379
- ①启动1个主机
- ①启动1个主机
- redis-server /usr/local/redis-6379/redis-6379.conf
- ②启动2个从机
- redis-server /usr/local/redis-26379/redis-26379.conf
- redis-server /usr/local/redis-26380/redis-26380.conf
- ③启动3个哨兵
- redis-sentinel /usr/local/redis-36379/redis-36379.conf
- redis-sentinel /usr/local/redis-36380/redis-36380.conf
- redis-sentinel /usr/local/redis-36381/redis-36381.conf
- ④查看哨兵信息
- ⑤演示主从切换
11-哨兵模式工作流程(掌握)
- 工作流程
- 监控
- 用于同步各个节点(master、slave、sentinel)的状态信息
- 通知
- 用来保持联通,发现故障
- 故障转移
- 发现老的master挂了
- 竞选哨兵负责人
- 优选新的master
- 新master上任,其它slave切换master,原master作为slave故障恢复后连接
- 监控
- 监控
- 通知
- 故障转移
12-Cluster集群概述(掌握)
- 存在问题?
- redis提供的服务OPS(operation per second)可以达到10万/秒,当前业务OPS如果超过10万/秒, 怎么办?
- 内存单机容量达到256G,当前业务需求内存容量1T,怎么办?
- cluster集群
- 作用
- 分散单台服务器的访问压力,实现负载均衡
- 分散单台服务器的存储压力,实现可扩展性
- 降低单台服务器宕机带来的业务灾难
13-Cluster集群配置及启动(掌握)
-
需求
- 搭建3个1主1从的主从结构,并启动集群.
-
集群语法
# 是否启用cluster,加入cluster节点 cluster-enabled yes|no # cluster配置文件名,该文件属于自动生成,仅用于快速查找文件并查询文件内容 cluster-config-file filename # 节点服务响应超时时间,用于判定该节点是否下线或切换为从节点 cluster-node-timeout milliseconds
#<master-host1>:<master-port1> ... <master-hostn>:<master-portn> : 主机的ip和port #<slave-host1>:<slave-port1> ... <slave-hostn>:<slave-portn> : 从机的ip和port #--cluster-replicas <n> : 1个master对应n个slave redis-cli --cluster create <master-host1>:<master-port1> ... <master-hostn>:<master-portn> <slave-host1>:<slave-port1> ... <slave-hostn>:<slave-portn> --cluster-replicas <n>
-
开发步骤
- ①配置3个主机
- redis-6379 , redis-6380 , redis-6381
- ②配置3个从机
- redis-26379 , redis-26380 , redis-26381
- ③启动3个主机,3个从机
- ④启用cluster集群服务
- ⑤查看cluster集群信息
- ①配置3个主机
-
①配置3个主机
port 6379 daemonize no dir "/usr/local/redis-6379/data" # 设置RDB文件 dbfilename "dump-6379.rdb" # 开启aof appendonly yes appendfilename "appendonly-6379.aof" appendfsync everysec # 设置无密码访问 protected-mode no # 开启集群 cluster-enabled yes # cluster配置文件名 cluster-config-file "cluster-6379.conf" # 节点服务响应超时时间为5秒 cluster-node-timeout 5000
-
②配置3个从机
# 6379的slave port 26379 daemonize no dir "/usr/local/redis-26379/data" # 设置RDB文件 dbfilename "dump-26379.rdb" # 开启aof appendonly yes appendfilename "appendonly-26379.aof" appendfsync everysec # 设置无密码访问 protected-mode no # 开启集群 cluster-enabled yes # cluster配置文件名 cluster-config-file "cluster-26379.conf" # 节点服务响应超时时间为5秒 cluster-node-timeout 5000
-
③启动3个主机,3个从机
- redis-server /usr/local/redis-6379/redis-6379.conf
- …
- redis-server /usr/local/redis-26379/redis-26379.conf
-
④启用cluster集群服务
redis-cli --cluster create 192.168.148.110:6379 192.168.148.110:6380 192.168.148.110:6381 192.168.148.110:26379 192.168.148.110:26380 192.168.148.110:26381 --cluster-replicas 1
-
⑤查看cluster集群信息
14-Cluster集群演示(掌握)
-
①使用集群操作set指令
#普通模式,不能适用于集群 redis-cli set name oldqiu get name
#集群模式 redis-cli -c set name oldqiu get name
-
②使用集群演示主从转换
- Cluster集群内置了哨兵模式
15-Cluster集群添加master节点(掌握)
-
需求
- 将redis-6382作为新主机添加到集群
-
语法
#<newhost>:<newport>:新主机的ip和port #<clusterhost>:<clusterport> : 已经在集群的主机的ip和port redis-cli --cluster add-node <newhost>:<newport> <clusterhost>:<clusterport>
-
开发步骤
- ①启动新的主机
- redis-6382
- ②将redis-6382添加到集群
- ①启动新的主机
-
①启动新的主机
port 6382 daemonize no dir "/usr/local/redis-6382/data" # 设置RDB文件 dbfilename "dump-6382.rdb" # 开启aof appendonly yes appendfilename "appendonly-6382.aof" appendfsync everysec # 设置无密码访问 protected-mode no # 开启集群 cluster-enabled yes # cluster配置文件名 cluster-config-file "cluster-6382.conf" # 节点服务响应超时时间为5秒 cluster-node-timeout 5000
redis-server /usr/local/redis-6382/redis-6382.conf
-
②将redis-6382添加到集群
redis-cli --cluster add-node 192.168.148.110:6382 192.168.148.110:6379
-
③查看cluster集群信息
16-Cluster集群添加slave节点(掌握)
-
需求
- 将redis-26382作为redis-6382的从机添加到集群
-
语法
#<new-slave-host>:<new-slave-port> : 新从机的ip和port #<master-host>:<master-port> : 主机的ip和port #<master-id> : 主机的id redis-cli --cluster add-node <new-slave-host>:<new-slave-port> <master-host>:<master-port> --cluster-slave --cluster-master-id <master-id>
-
开发步骤
- ①启动从机
- redis-26382
- ②将redis-26382添加到集群
- ③查看cluster集群信息
- ①启动从机
-
①启动从机
# 6379的slave port 26382 daemonize no dir "/usr/local/redis-26382/data" # 设置RDB文件 dbfilename "dump-26382.rdb" # 开启aof appendonly yes appendfilename "appendonly-26382.aof" appendfsync everysec # 设置无密码访问 protected-mode no # 开启集群 cluster-enabled yes # cluster配置文件名 cluster-config-file "cluster-26382.conf" # 节点服务响应超时时间为5秒 cluster-node-timeout 5000
redis-server /usr/local/redis-26382/redis-26382.conf
-
②将redis-26382添加到集群
redis-cli --cluster add-node 192.168.148.110:26382 192.168.148.110:6382 --cluster-slave --cluster-master-id d6535c1c2e2bca5eeac585b123b101dee019ba00
-
③查看cluster集群信息
17-Cluster集群移除节点(掌握)
-
语法
#<node-host>:<node-port> <node-id> : 节点的ip和port,id redis-cli --cluster del-node <node-host>:<node-port> <node-id>
-
需求
- ①移除redis-26382
- ②移除redis-6382
-
代码实现
redis-cli --cluster del-node 192.168.148.110:26382 8048d65a162fa7be45a12dc21622075d8ac4cf19
redis-cli --cluster del-node 192.168.148.110:6382 d6535c1c2e2bca5eeac585b123b101dee019ba00
18-Cluster集群分槽(掌握)
-
概述
- redis总共有16384个槽,需要分配给对应master主机.
-
语法
#<target-master-host>:<target-master-port> : 目标主机的ip和port #<source-master-id1> ... <source-master-idn> : 源主机的id #<target-master-id> : 目标主机的id #<n>:分配n个槽 redis-cli --cluster reshard <target-master-host>:<target-master-port> --cluster-from <source-master-id1> ... <source-master-idn> --cluster-to <target-master-id> --cluster-slots <n>
-
需求
- 将redis-6379 , redis-6380 ,redis-6381的4096个槽分配给redis-6382
-
代码实现
redis-cli --cluster reshard 192.168.148.110:6382 --cluster-from 38f366d6ff8af4bf8cfceba5bec440d653473a6f ab178720e72231d54d9d877f032c1eb855c54e32 57068d471add84cd4218f9b0055a804e5167e785 --cluster-to d6535c1c2e2bca5eeac585b123b101dee019ba00 --cluster-slots 4096