# Redis_Sln **Repository Path**: lztkdr/redis_sln ## Basic Information - **Project Name**: Redis_Sln - **Description**: No description available - **Primary Language**: C# - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2021-07-03 - **Last Updated**: 2022-02-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Redis 学习 [Redis 教程 | 菜鸟教程 (runoob.com)](https://www.runoob.com/redis/redis-tutorial.html) Remote Dictionary Server 远程字典的服务 内存数据库 * 方便扩展 * 大数据量高性能 * 九大结构 * 分布式存储 # 常用命令 ``` shell config get * # 查看当前redis服务器的 所有配置 config get configName # 查看当前redis服务器的 configName配置 config set configName configValue # 设置当前redis服务器的 configName info # 查看当前redis服务器信息 包含Server/Clients/Memory/Stats/Replication/CPU/Cluster ... info replication # 查看当前redis服务器的 主从角色的架构信息 keys * # 一方面 KEYS * 命令可以列出所有的键,会影响数据安全 # 另一方面 KEYS 命令会阻塞数据库,在数据库中存储了大量数据时,该命令会消耗很长时间 slaveof 127.0.0.1 7010 # 命令可以将当前服务器转变为指定服务器的从属服务器(slave server) ``` # 环境部署 # benchmark压测 ## Redis环境性能测试 https://www.runoob.com/redis/redis-benchmarks.html ``` shell redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 redis-benchmark -h 127.0.0.1 -p 6086 -c 50 -n 10000 -t get redis-benchmark -h 51try.top -p 6300 -c 50 -n 10000 redis-benchmark -h 127.0.0.1 -p 6379 -t set,lpush -n 10000 -q ``` ![1625296486817](media/1625296486817.png) # IO多路复用 ngixn + redis 是用了多路复用 。 Redis 安装到windows下是不是也不能多路复用? 多路复用底层 是 系统提供的。 windows下使用的是select 模型,只能使用select模型,最大能并发能支持1024个请求连接。不能利于多路复用的epool模型,linux下使用的是epool模型。 ## redis 解决了两个问题 1. 直接操作硬盘的问题 2. 连接阻塞的问题 之前是io阻塞,系统在等待请求的时候,不能干其他的事情,而且每次只能阻塞等待请求,redis通过多路复用,由系统帮你监听多个连接,只要有事情要处理 ,系统会调用你的回调函数,把监听的事情交给了系统,释放了我们自己的代码。 # 5大常用结构+4种特殊结构 ## 5大常用结构 ### String 字符串 这是一个简单的键值对操作。。 redis 本身是一个内存数据,内存中的数据越来越多的时候,内存不是不够用了吗。可以给数据设置过期时间。。 过期时间设置-1,是过多久过期,2是可以直接设置具体的日期 #### 使用场景 1. session 利用redis做session共享内存 2. 自增和自减法 -- 做一些网站的请求数量,或者论坛的点赞数、评论数、库存数,不可能每次都去执行数据库。。。 可以直接利用redis,操作内存,只不过到最后,做个数据刷盘,把这些统计数据放到我们硬盘中。。 ``` shell set name clay object encoding name # 查看name对应值的存储形式,embstr/raw/int/ziplist... ``` #### string底层 在功能中,出了非必要的情况,除了上述这几个需求,尽量不要使用string类型 底层会浪费大量的内存空间 如果使用raw编码,则每次开辟空间都会留一些空间,如果数据长度变了,则内存也会继续变大。。 如果你使用embstr :它每次最多开辟64个字节的空间,只有44个字节时存储我们数据的。 如果你在操作的redis的时候,内容长度小于等于44,则会自动选择embstr编码开辟空间 如果你操作redis的时候,内容长度大于44的,使用ram编码,浪费 空间 还有一个int:只是针对于写的数据是数值,才行。。切记只有整型才是int类型 底层就是因为开辟的组件的原因。。。所以会浪费空间,尽量不要使用string。。除非你不在乎那些内存,就这样搞。。。 为什么要这样设置,给你一种选择。。。 ### Hash 哈希 官网推荐使用hash 。。。 #### 使用场景 储存一个用户信息 id,name,address,email 如果使用string去存储,key=userid values={id:1,name:'',address:'' ====} 如果需要修改其中木一个值, 先把数据拿到代码内存,然后反序列化,然后修改值,然后序列化--存储到redis 如果直接使用hash,则值需要制定修改具体某一个字段的值就可以了 #### hash底层 ziplist,hash ? ziplist:压缩版的list ..(动态的数组,数组的每一个元素的空间是一样的) 第一问题:每次插入都有开辟空间,连续的 第二问题:你要查询的时候,你要从头来计算,--查询的速度变慢。。。 hash的数据结果,它的时间复杂度是O(1); hash快速的查询结果,而且节省空间,一上来就是一个小量的hash。。 问题:如果hash后面链表越来越长的时候,时间复杂度是不是又变高。。。 解决了这个问题?? 扩容:是一开始的时候,redis有两个hash结构在存储数据,第一次只要一个是有长度,一个是没有长度。。数据迁移问题来了,因为如果不小心操作的时候,刚好触发了瓶颈,要扩容, 解决迁移问题: 第一个:不是一次性迁移完成,是每一次操作只会迁移一部分。 第二个:是一个后台任务,后台任务给你偷偷摸摸的迁移数据: 非常重要:数据一致的问题::自己有机会了解一下 ### List 集合 第一个是队列(先进先出) 第二个是栈:(先进后出) 第三个就是普通的集合。 #### 使用场景 如果你要做插队,或者做队列,都可以使用list 集合设置过期,只能给整个集合设置,不能单独给某一个元素设置,没有给单独元素设置过期时间的策略。。 遇到分页场景也可以使用。。。 如果不想用list做消息队列,也可以直接用提供的专门的消息发布来做。。 特殊情况,公司不想要新的技术栈,就想用redis ### Set 去重集合 也是一个集合,只不过是一个去重的集合。 #### 使用场景 比如我需要做投票-- 根据ip地址来投票,每一个ip只投一票。。。如果用list,需要我们自己来判断。如果使用set,则系统自动会去重。。。 取交集和去并集 a:{1,2,3,4,5} b:{2,3,4,5,6} a和b的交集= {2,3,4,5} a和b的并集={1,2,3,4,5,6} qq好友推荐里面用的比较多 ### Zset 去重集合+分数 也是去重的集合,具有set的功能,而且在这些功能里面加了一个值,分数。 #### 使用场景 服务注册于发现,抽奖。。。限流。。排行榜 。。。。 用的比较多的场景。 提供了一个非常强大的功能,自动排序。 zset默认是顺序查询,--我们默认查询是先根据分数来查询我们数据,我们的业务是尽量使用分数来查询。 #### 限流示例 ``` lua 1.内存满了,会触发数据淘汰策略,释放资源 2.限流--讲lua,扩展,看一下实现代码 ### 限流lua代码如下 local times = redis.call('incr',KEYS[1]) if times == 1 then redis.call('expire',KEYS[1], ARGV[1]) end if times > tonumber(ARGV[2]) then return 0 end return 1 ``` ``` shell redis-cli --eval limit.lua rate.limit:127.0.0.1 , 5 6 ``` ``` csharp /// /// 滑窗式限流 利用redis的Zset类型 /// /// public bool LimitFlow() { DateTime currentTime = DateTime.Now; using (IRedisClient client = redisService.GetRedisClient()) { // 判断是否存在响应的key if (client.ContainsKey("服务名称")) { //获取前一秒和当前秒时间类的集合总数量 //此数量表示的就是请求数量 int count = client.GetRangeFromSortedSetByLowestScore("服务名称", currentTime.AddSeconds(-1).ToFileTimeUtc(), currentTime.ToFileTimeUtc()).Count; // 8表示的是限流的数量 //如果请求数量大于限流数量,则返回false,可以拒绝请求 if (count != null && count >8) { return false; } } //如果是第一次获取请求数量 //或者满足限流限制,则返回true,然后给Zset中添加一次请求数量,分数就是当前的时间戳 client.AddItemToSortedSet("服务名称", Guid.NewGuid().ToString(), currentTime.ToFileTimeUtc()); return true; } } ``` ## 4种特殊结构 ### BitMaps 实现对位的操作。 是一个节省内存的数据结构。。0,1 介绍:[Bitmaps](https://blog.csdn.net/weixin_30402085/article/details/95181006) ### Hyperlogloss 一种不太准确的基数统计方法。 不精确的去重计数方案,虽然不精确但是也不是非常不精确,标准误差是0.81%。 网页浏览统计,访问量等等这些不是特别需要准确的数据,可以利用它。 ### Streams 支持多播的可持久化的消息队列,用于实现发布订阅功能。 kafak的生产者和消费者的模型都是仿照它做的。 ### Bloom Filter 布隆过滤器 1. 存储数据----占用很少的内存存储跟多的数据。。 2. 判断数据在不在存储的里面 ---存在一个查询效率的问题 ### GEO 用于存储地理位置信息,并对存储的信息进行操作。 不同key存储的就是一个经度和纬度。可以计算俩个key之间的距离。 # 事务 + Lua ## 关系型数据库的事务 保证执行多个指令的时候,要么都执行,要么都不执行,要么都成功,要么都没有成功。 (ACID) 原子性:就是最小的单位 一致性 :好多命令,要么全部执行成功,要么全部执行失败 隔离性:一个会话和另一个会话之间是互相隔离 持久性:执行就执行了,数据保存在硬盘 ## Redis事务 提交--没有回滚,提交了就提交,成功或者不成功,只会返回一个结果。 ``` csharp //using ServiceStack.Redis; //using System; //事务模式 using (RedisClient client = new RedisClient("127.0.0.1", 6300, "63796379")) { //删除当前数据库中的所有Key 默认删除的是db0 client.FlushDb(); //删除所有数据库中的key client.FlushAll(); client.Set("a", "1"); client.Set("b", "1"); client.Set("c", "1"); //获取当前这三个key的版本号 实现事务 //要把所有要更新的key进行监视,才能起到完整的事务,实现ACID效果。 // 这里有别于 批处理,不做watch的话,跟批处理一个原理。 client.Watch("a", "b", "c"); using (var trans = client.CreateTransaction()) { trans.QueueCommand(p => p.Set("a", "3")); trans.QueueCommand(p => p.Set("b", "3")); trans.QueueCommand(p => p.Set("c", "3")); var flag = trans.Commit(); Console.WriteLine(flag); } //根据key取出值,返回string Console.WriteLine( client.Get("a") + ":" + client.Get("b") + ":" + client.Get("c") ); } ``` redis没想把事情搞的复杂,所以redis里面使用的事务,没有锁(其他命令执行不会被阻塞,依旧会执行)。 ### Lua Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。 ``` 建议:如果要使用redis的事务,则需要和Watch方法一起使用,而且最好,监听的这些字段都包含在所有的key里面。。 小菜鸟玩事务,大神搞lua Redis ,直接支持lua脚本 整个lua脚本的代码,本身就具有原子性。。 多线程--- 并发的问题。。 ``` 可以把lua脚本理解成 存储过程,性能是比sql语句还要高的,而且具备原子性。 * **扩展疑问:那么 关系型数据库 的存储过程 能够代替 事务?** * **扩展疑问:那么存储过程里 使用事务,能否代替程序代码的事务?** ``` csharp //using ServiceStack.Redis; //using System; using (var client = new RedisClient("127.0.0.1", 6300, "63796379")) { //Console.WriteLine(client.ExecLuaAsString(@"return redis.call('get','name')")); //库存 //Console.WriteLine(client.ExecLuaAsString(@"redis.call('set','number','5')")); // number 库存数量、ordercount 订单数量 // INCR 增量1 // DECR 减量1 var lua = @"local count = redis.call('get',KEYS[1]) if(tonumber(count)>=0) then redis.call('INCR',ARGV[1]) return redis.call('DECR',KEYS[1]) else return -1 end"; Console.WriteLine(client.ExecLuaAsString(lua, keys: new[] { "number" }, args: new[] { "ordercount" })); } ``` 可以把lua理解成数据存储过程,但是实质不一样,而且lua具有原子性,而且性能要比我们直接操作指令性能要高---lua是c语言编写的,redis也是c语言编写,不存在编译过程。 # 持久化 ## 快照的方式(RDB) 每次都去抓拍一下全面貌,进行都全量备份,默认生成 **dump.rdb**文件。 ### RDB持久化策略 ![1625389012448](media/1625389012448.png) ### RDB手动备份策略 ![1625389716739](media/1625389716739.png) #### save 阻塞线程--- 因为执行这个指令的线程就是redis里面唯一的那个执行指令的线程。 将内存数据镜像保存为rdb文件,由于redis是单线程模型,当你备份大量的数据的时候,如果耗时比较长,期间会阻塞redis服务进程,redis服务不再处理任何指令,直到rdb文件创建完成为止。 **所以 使用时 应考虑其他命令会被阻塞的情况,慎重使用 !!** #### bgsave 单独的线程,后台专门有一个线程去实现备份。 这个后台的就是专门来根据我们的配置文件里策略然后去备份数据文件。 父进程启动一个子进程,由子进程将内存保存在硬盘文件,期间不会影响其他的指令操作。 ##### 底层原理 有一个定时器,就是不停的计算我们阈值(如: save 900 1)。每一次bgsave之后,要把计数器清0 备份缓慢,但是重启加载数据性能比较快。 ##### 优势 备份缓慢,但是重启加载数据性能比较快。。。 ## 追加方式(AOF) 在之前的基础上追加,默认生成 **appendonly.aof**文件。 ![1625390590413](media/1625390590413.png) ``` shell config get appendonly ``` 默认 是不开启的,需要修改配置文件,修改config 中的配置 appendonly yes 。 ### AOF 持久化策略 #### 三种追加方式 appendfsync always 只要有读写。 appendfsync everysec 1s钟的周期(默认)。 appendfsync no 等业务不繁忙的时候,这种操作最不可靠,一般不用。 ![1625391201387](media/1625391201387.png) #### 日志追加 基本都是顺序读写,本身要比我们操作关系型数据库要快(mysql)。 #### 优缺点 文件可读性比较高,每次追加修改的东西,备份比较快,但是当服务重启的时候,加载数据的时候比较慢---素描 。 ![1625391673675](media/1625391673675.png) ### 混合模式 持久化 #### 配置 ``` shell aof-use-rdb-preamble yes # aof/rdb 混合模式 appendonly yes # aof开启 ``` #### 手动调用 ``` shell bgrewriteaof ``` ![1625393089795](media/1625393089795.png) 如果rdb和aof都开启了,则默认首先加载aof文件,为了保证数据的尽可能完整性。。 aof 重写问题: ![](media/1625392143262.png) ![1625392182292](media/1625392182292.png) 当数据文件到达一个阈值的时候,我们的文件会继续重写变成一个小文件。。 rdb:备份慢,启动快 aof:备份快,启动慢 redis..4X 版本之后,混合模式--- 混合模式【备份快,启动快】 备份的时候,我通过aof备份,我还原的时候根据rdb... 架构师的思想理念--怎么设计---(后台有一个任务,专门负责吧aof文件转化成rdb文件) aof--文件重写的问题---到数据大的时候, 当我们进行aof操作的时候,把aof文件变成rdb,然后之后的操作直接取做日志追加。。 默认的情况下,你要你开启了aof则,混合默认自动打开。 混合模式的文件--包含aof和rdb; ![1625393128063](media/1625393128063.png) ### 补充说明 如果要redis的性能非常高,就不要持久化 如果要保证数据的完整性,要根据自己的业务来选择不同的持久化策略,一般情况都是使用混合模式 第一次配置的时候,切记吧aof打开(默认开启混合模式)。 # 主从架构 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器,主从是哨兵和集群模式能够 实施的基础。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点 到从节点。 一主一从 一主多从 树状主从 在从服务器执行下面的命令成为或取消成为某节点的从节点 ``` shell #slaveof 主服务器的IP 端口号 slaveof host port ``` 密码相关配置 ``` shell requirepass <当前redis 密码> masterauth ``` ![1625393260183](media/1625393260183.png) ``` shell docker run -d -p 5001:6379 --name redis1 redis #主 docker run -d -p 5002:6379 --name redis2 redis #从 docker run -d -p 5003:6379 --name redis3 redis #从 docker exec -it redis1 /bin/bash docker exec -it redis2 /bin/bash docker exec -it redis3 /bin/bash ``` 主从是实现读写分离,然后主节点会把数据同步到从节点里面。 ## 同步原理 保存主节点信息:从节点在配置slaveof之后,会在保存主节点的信息。 主从建立socket连接:定时发现主节点以及尝试建立连接。 发送ping命令:从节点定时发送ping给主节点,主节点返回ping。若主节点没有返回ping或因阻塞无法响应导致超时,则主从断开,在下次定时任务时会从新ping主节点。 同步数据集:首次连接,主到从,全量同步。 命令持续复制:全量同步完成后,保持增量同步,增量的过程会有一个偏移量。 如果在同步的过程发现了非常严重的问题,它会进行全量同步。 ## 补充说明 用法是实现高可用,主节点宕机了,从节点可以替换主节点,不是自动替换,需要程序员修改代码,改ip地址和端口,或者应该是用keeplive 用vip地址来高可用。。。主从是不会自动切换--如果要自动切换要使用哨兵。。。 ![1625396276761](media/1625396276761.png) 注意点:一般情况下,我们使用主从的时候,防止数据不一致,从节点只能读,不让他写。 默认从节点不会进行写操作。。。 所以当主节点宕机了,我们需要改代码,收到去修改redis的配置。。 ## 疑问 - 主从模式是不是在生产环境中一般不用?因为主节点挂了,需要人工干预。 **是的,一般情况下都用哨兵。** # Redis哨兵 [Redis哨兵(Sentinel)模式](https://www.jianshu.com/p/06ab9daf921d) **主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。**这不是一种推荐的方式,更多时候,我们优先考虑**哨兵模式** 哨兵(sentinel),用于对主从结构中的每一台服务器进行监控,当主节点出现故障后通过投票机制来挑选新的主节点,并且将所有的从节点连接到新的主节点上。 - 监控: 监控主从节点运行情况。 - 通知: 当监控节点出现故障,哨兵之间进行通讯。 - 自动故障转移: 当监控到主节点宕机后,断开与宕机主节点连接的所有从节点,然后在从节点中选取一个作为主节点,将其他的从节点连接到这个最新的主节点。最后通知客户端最新的服务器地址。 **哨兵,最少是三台服务器,必须是单数(投票,防止平票的情况)--- 官方推荐。** 哨兵起到的代理作用,所以应用程序代码层面的连接,首先连接的是 哨兵地址。 哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是**哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。** ![1625401134199](media/1625401134199.png) 常见搭建: | 服务类型 | 是否是主服务器 | IP地址 | 端口 | | -------- | -------------- | -------------- | ----- | | Redis | 是 | 192.168.11.128 | 6379 | | Redis | 否 | 192.168.11.129 | 6379 | | Redis | 否 | 192.168.11.130 | 6379 | | Sentinel | - | 192.168.11.128 | 26379 | | Sentinel | - | 192.168.11.129 | 26379 | | Sentinel | - | 192.168.11.130 | 26379 | ![1625408712585](media/1625408712585.png) ## 宕机情况 1. 主观宕机:单独哨兵认为你宕机了,发现了故障。。 2. 客观宕机:半数哨兵认为主节点宕机,发现了故障。 ## 选举主节点的原则 1. 健康度。。从节点响应的时间。 2. 完整性。根据我们从节点备份的完整性,根据数据备份偏移量。偏移量越高,代表当下数据越完整。 3. 稳定性。根据启动时间周期,心跳检测。 4. 如果上面三个条件都相等,则根据我们节点启动时分配的run id来,如果run id越小,则最有可能选择为我们主节点。 ## 故障转移 0. Leader最开始是没有的,达到半数以上的哨兵都认为主节点宕机的时候,最先发现宕机的这个哨兵就是Leader。 1. 哨兵Leader 根据一定规则从各个从节点中选择出一个节点升级为主节点; 2. 其余从节点修改对应的主节点为新的主节点; 3. 当原主节点恢复启动的时候,变为新的主节点的从节点。 ## 哨兵配置 1主/2从/3哨兵 ### Sentinel配置 Sentinel配置文件路径: ```shell mkdir /usr/local/etc/redis/sentinel/sentinel -p cd /usr/local/etc/redis/sentinel/sentinel ``` #### redis-sentinel-1.conf ``` shell # bind 127.0.0.1 # 哨兵的端口号 # 因为各个哨兵节点会运行在单独的Docker容器中 # 所以无需担心端口重复使用 # 如果需要在单机 port 26379 # 设定密码认证 requirepass 123456 # 配置哨兵的监控参数 # 格式:sentinel monitor # master-name是为这个被监控的master起的名字 # ip是被监控的master的IP或主机名。因为Docker容器之间可以使用容器名访问,所以这里写master节点的容器名 # redis-port是被监控节点所监听的端口号 # quorom设定了当几个哨兵判定这个节点失效后,才认为这个节点真的失效了 sentinel monitor local-master 127.0.0.1 6379 2 # 连接主节点的密码 # 格式:sentinel auth-pass sentinel auth-pass local-master 123456 # master在连续多长时间无法响应PING指令后,就会主观判定节点下线,默认是30秒 # 格式:sentinel down-after-milliseconds sentinel down-after-milliseconds local-master 30000 ``` #### redis-sentinel-2.conf ``` shell port 26380 requirepass 123456 sentinel monitor local-master 127.0.0.1 6379 2 sentinel auth-pass local-master 123456 # master在连续多长时间无法响应PING指令后,就会主观判定节点下线,默认是30秒 # 格式:sentinel down-after-milliseconds sentinel down-after-milliseconds local-master 30000 ``` #### redis-sentinel-3.conf ``` shell port 26381 requirepass 123456 sentinel monitor local-master 127.0.0.1 6379 2 sentinel auth-pass local-master 123456 # master在连续多长时间无法响应PING指令后,就会主观判定节点下线,默认是30秒 # 格式:sentinel down-after-milliseconds sentinel down-after-milliseconds local-master 30000 ``` ### Redis配置 ``` shell mkdir /usr/local/etc/redis/sentinel/server -p cd /usr/local/etc/redis/sentinel/server ``` #### redis-master.conf ``` shell # 监听端口 port 6379 # 启动时不打印logo # 这个不重要,想看logo就打开它 always-show-logo no # 设定密码认证 requirepass 123456 # 禁用KEYS命令 # 一方面 KEYS * 命令可以列出所有的键,会影响数据安全 # 另一方面 KEYS 命令会阻塞数据库,在数据库中存储了大量数据时,该命令会消耗很长时间 # 期间对Redis的访问也会被阻塞,而当锁释放的一瞬间,大量请求涌入Redis,会造成Redis直接崩溃 rename-command KEYS "" ``` #### redis-slave1.conf ``` shell # bind 127.0.0.1 # 监听端口 port 6380 always-show-logo no requirepass 123456 rename-command KEYS "" slaveof 127.0.0.1 6379 # 设定连接主节点所使用的密码 masterauth "123456" ``` #### redis-slave2.conf ``` shell # 监听端口 port 6381 always-show-logo no # 设定密码认证 requirepass 123456 rename-command KEYS "" slaveof 127.0.0.1 6379 # 设定连接主节点所使用的密码 masterauth "123456" ``` ### Sentinel的Docker-Compose.yml文件 ``` shell cd /usr/local/etc/redis/sentinel/sentinel vi docker-compose.yml ``` ``` shell version: '3' services: redis-sentinel-1: image: redis container_name: redis-sentinel-1 restart: always # 为了规避Docker中端口映射可能带来的问题 # 这里选择使用host网络 network_mode: host volumes: - ./redis-sentinel-1.conf:/usr/local/etc/redis/redis-sentinel.conf # 指定时区,保证容器内时间正确 environment: TZ: "Asia/Shanghai" command: ["redis-sentinel", "/usr/local/etc/redis/redis-sentinel.conf"] redis-sentinel-2: image: redis container_name: redis-sentinel-2 restart: always network_mode: host volumes: - ./redis-sentinel-2.conf:/usr/local/etc/redis/redis-sentinel.conf environment: TZ: "Asia/Shanghai" command: ["redis-sentinel", "/usr/local/etc/redis/redis-sentinel.conf"] redis-sentinel-3: image: redis container_name: redis-sentinel-3 restart: always network_mode: host volumes: - ./redis-sentinel-3.conf:/usr/local/etc/redis/redis-sentinel.conf environment: TZ: "Asia/Shanghai" command: ["redis-sentinel", "/usr/local/etc/redis/redis-sentinel.conf"] ``` ### Redis的Docker-Compose.yml文件 ```shell cd /usr/local/etc/redis/sentinel/server vi docker-compose.yml ``` ``` shell version: '3' services: # 主节点的容器 redis-server-master: image: redis container_name: redis-server-master restart: always # 为了规避Docker中端口映射可能带来的问题 # 这里选择使用host网络 network_mode: host # 指定时区,保证容器内时间正确 environment: TZ: "Asia/Shanghai" volumes: # 映射配置文件和数据目录 - ./redis-master.conf:/usr/local/etc/redis/redis.conf - ./data/redis-master:/data command: ["redis-server", "/usr/local/etc/redis/redis.conf"] # 从节点1的容器 redis-server-slave-1: image: redis container_name: redis-server-slave-1 restart: always network_mode: host depends_on: - redis-server-master environment: TZ: "Asia/Shanghai" volumes: - ./redis-slave1.conf:/usr/local/etc/redis/redis.conf - ./data/redis-slave-1:/data command: ["redis-server", "/usr/local/etc/redis/redis.conf"] # 从节点2的容器 redis-server-slave-2: image: redis container_name: redis-server-slave-2 restart: always network_mode: host depends_on: - redis-server-master environment: TZ: "Asia/Shanghai" volumes: - ./redis-slave2.conf:/usr/local/etc/redis/redis.conf - ./data/redis-slave-2:/data command: ["redis-server", "/usr/local/etc/redis/redis.conf"] ``` ### 启动 ``` shell mkdir /usr/local/etc/redis/sentinel -p cd /usr/local/etc/redis/sentinel # 然后把 sentinel、server 文件夹上传上去。 ``` sentinel、server 文件夹都复制到cd /usr/local/etc/redis/sentinel 中 ![1625416308362](media/1625416308362.png) ```shell cd /usr/local/etc/redis/sentinel/server docker-compose up -d cd /usr/local/etc/redis/sentinel/sentinel docker-compose up -d #查看主从信息 info replication #测试 #停止主节点 docker stop redis-server-master #进入从1redis-server-slave-1查看角色是否被修过为主节点 docker exec -it redis-server-slave-1 /bin/bash redis-cli -p 6380 -a 123456 info replication #进入从1redis-server-slave-2查看角色是否被修过为主节点 docker exec -it redis-server-slave-2 /bin/bash redis-cli -p 6381 -a 123456 info replication ``` ## 宕机的主节点恢复问题 主节点宕机,从节点替换主节点,然后过一会原主节点恢复了,则我们的原主节点变成从节点。 哨兵或者主从里面的数据问题: ### 1. 脑裂问题 出现了主节点和哨兵之间网络原因,而且有多数以上的哨兵认为主节点宕机,则再从会从节点现在一个主,这个时候客户端代码还是可以连接到之前的主节点的,可以写数据,此时哨兵选举了新的主节点,然后之前的主网络恢复了,然后之前的主节点备份现在的主节点数据,造成数据不完整。。。 ### 2. 异步复制数据丢失问题 因为是异步复制数据,如果主节点和从节点直接数据复制太慢,在这之间主节点宕机,而且是真的宕机,这个时候从节点替换主节点,丢失了数据。。 哨兵--不管怎么样的配置都没有办法保证数据百分之白不丢失,只能尽可能少量丢数据 ### 怎么解决上面这来个问题呢? 需要改配置文件: 1. 至少有几个从节点。配置=0,代表的是,当主节点和从节点之间互通的时候,发现从节点小于一个的时候,则从节点不会再继续给客户端提供服务。。 解决脑裂问题。。 2. 偏移量配置。。。主节点和从节点数据之前偏移量只差,如果偏移量只差比配置小,则主节点也不会提供服务。。 ![1625503959519](media/1625503959519.png) 第一个配置是主节点最少能连接到几个从节点 第二配置是主节点和从节点的ack时间差-响应(数据复制完整性问题,偏移量) # 集群 多个节点提共服务。 - 副本集的集群 多个节点都可以读都可以写,而且多个节点之间数据是需要一致的。 (每个节点都可读可写,为保证一致性,对于1条数据来说,不但1个节点写入,要在多个节点上写入) 有大量的数据冗余。。 kafka... tidb 数据一致性问题需要解决--- 每个节点都负责所有的数据。 - 分片模式 每一个节点只负责一部分的数据写。 **所以我门的哨兵模式,需要在测试环境和生产环境中,要去测试的,如果不测是,只是简单的修改配置文件,就用上去,那么这哨兵这东西可能就不好用了!** 注意:哨兵无法解决,主从切换过程中数据不丢失的问题。 主节点宕机,数据写不进去,不叫数据丢失。从节点没有把数据从主节点备份过来,这个叫数据丢失。 nosql/分布式/项目实战/性能调优/数据结构和算法。 常用的 主从 集群 哨兵 哪个更常用?集群 集群/哨兵 是在 主从架构 上的升级。 # 数据结构使用场景和底层数据原理(Hash,跳跃表,ziplist)