redis的删除策略、主从复制、哨兵


1、redis的删除策略

   Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态,返回值:

    XX :具有时效性的数据;  

    -1 :永久有效的数据 ;

      -2 :已经过期的数据 或 被删除的数据 或 未定义的数据 ;

  提问1:过期了的数据真的删除了吗?

  首先要知道redis针对设置expire数据的三种删除策略:

  1)定时删除, 创建一个定时器,当key设置有过期时间,且过期时间到达时,立即执行对键的删除操作 。特点:拿时间换空间。虽然节约内存,但会增加cpu压力。

  2)惰性删除,数据到达过期时间,不做处理。等下次访问该数据时:如果未过期,返回数据 ;发现已过期,删除,返回不存在。特点拿空间换时间。

  3)定期删除,是以上两种的折中策略,其实删除策略就是在内存占用和cpu占用找一种平衡。像定时删除和惰性删除都有可能降低redis性能。

     Redis启动服务器初始化时,读取配置server.hz的值,默认为10 ,在命令行中输入info server可以查询到。  

  每秒对redis中16个expires执行10次轮询,对expires中的过期数据检查,随机挑w个key检查,如果超时删除key,如果删除量超过一定值,会继续对这个expires进行检查,然后删除过期值;如果删除量小会对下一个expirs进行检查,以此类推。

  提问2:内存中都是有效数据,当插入新的数据时,内存不足怎么办

     Redis使用内存存储数据,在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法。 

    那么按照什么样的策略?

    第一种,在expires种挑选数据,也就是设定有过期时间的数据

    1)选择一定时间内使用次数最少的数据进行删除(LFU)

    2)选择一定时间内最长时间没有使用的进行删除(LRU)(推荐)

    3)选择将要过期的数据进行删除(TTL)

    4)任意选择

    第二种,在所有数据种,不管有期还是无期

    1)选择一定时间内使用次数最少的数据进行删除(LFU)

    2)选择一定时间内最长时间没有使用的进行删除(LRU)

    3)任意选择

    第三种,放弃逐出策略,no-enviction

 

    逐出算法的相关配置

#占用物理内存的比例,默认值为0,表示不限制。 
maxmemory
#每次选取待删除数据的个数 maxmemory
-samples
#达到最大内存后的对被挑选出来的数据进行删除的策略 #默认 no
-enviction:禁止驱逐数据 maxmemory-policy

 2、高级数据结构

  Bitmaps:用于状态信息统计,一个bit八位,每一位可以表示一个状态,如果我们想统计一个单位的党员,比如“0000 0000”,就表示没有一个党员;“0011 0110”表示3号、4号、6号、7号是党员。

  二进制位的基本操作

//设置一个key它的第0位是1
setbit bitkey 0 1
//第二位是1
setbit bitkey 2 1
//获取第二位上的状态位
getbit bitkey 2

  业务场景:

    某电影网站统计某一天/一周/一个月/一年有多少电影被点播,有多少电影没有被点播。比如一天的统计“1100 1110”,我们得知《武林外传》的id为1,查看二进制位为“1”,说名一天内有被点播,还可以从“1”个数得出一共有5部电影被点播,3部电影没有被点播。如果想统计每周的数据,只需要将每天的二进制位按位或,得到的二进制位就是一周的点播状态。如法炮制,可以统计一年的电影播放状态,如果某个二进制位是“0”,说明该电影一年内没有被点播,可以考虑下架。节省存储空间。

//对指定的key按位或/按位与/按位/异或/将结果保存在destkey中
bitop  op  destkey  key1  key2....
//统计指定key中某一范围中1的数量
bitconut key start end

 

  hyperloglog :用来做基数统计。

  {1, 3, 5, 7, 5, 7, 8}     基数集: {1, 3, 5 ,7, 8}   基数:5 

#添加数据
127.0.0.1:6379> pfadd id 001
(integer) 1
127.0.0.1:6379> pfadd id 002
(integer) 1
127.0.0.1:6379> pfadd id 002
(integer) 0
127.0.0.1:6379> pfadd id 002
(integer) 0
#统计数据
127.0.0.1:6379> pfcount id
(integer) 2
#合并数据
127.0.0.1:6379> pfmerge destkey id
OK

  GEO:有关地理位置的数据结构,例如已知A点和B点,求两点的距离。

  基本操作:

#添加数据,其中“key”可以看作是一个容器, 
geoadd key longitude latitude member [longitude latitude member ...]
#获取某个点的位置 
geopos key member [member ...]
#计算两点的距离,默认单位为米 
geodist key member1 member2 [unit] 
127.0.0.1:6379> geoadd geos 1 2 A
(integer) 1
127.0.0.1:6379> geoadd geos 1 3 B
(integer) 1
127.0.0.1:6379> geopos geos A
1) 1) "0.99999994039535522"
   2) "2.0000001856465488"
127.0.0.1:6379> geopos geos B
1) 1) "0.99999994039535522"
   2) "3.00000091215010656"
127.0.0.1:6379> geodist geos A B
"111226.3808"
127.0.0.1:6379> geodist geos A B km
"111.2264"
127.0.0.1:6379> 

  进阶操作:想知道附近有多少人

//把坐标点放入容器中
127.0.0.1:6379> geoadd geos 0 0 A
(integer) 1
127.0.0.1:6379> geoadd geos 0 1 B
(integer) 1
127.0.0.1:6379> geoadd geos 0 2 C
(integer) 1
127.0.0.1:6379> geoadd geos 1 0 D
(integer) 1
127.0.0.1:6379> geoadd geos 1 2 F
(integer) 1
127.0.0.1:6379> geoadd geos 2 0 G
(integer) 1
127.0.0.1:6379> geoadd geos 2 1 H
(integer) 1
127.0.0.1:6379> geoadd geos 2 2 I
(integer) 1
127.0.0.1:6379> geoadd geos 1 1 E
(integer) 1
//获取E点180km范围内有多少点
127.0.0.1:6379> georadiusbymember geos E 180 km
1) "A"
2) "D"
3) "B"
4) "E"
5) "G"
6) "H"
7) "C"
8) "F"
9) "I"
127.0.0.1:6379> georadiusbymember geos E 100 km
1) "E"
127.0.0.1:6379> georadiusbymember geos E 150 km
1) "D"
2) "B"
3) "E"
4) "H"
5) "F"

 3、主从复制

   单机redis会有很多风险,比如机器故障,丢失数据;单台机器容量有限,不可能无限制增加内存;当读操作频率远远大于大于写操作频率时,单台结点负载过大。

  解决方案:准备多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上,连接在一起,并保证数据是同步的。即使有其中一台服务器宕机,其他服务器依然可以继续 提供服务,实现Redis的高可用,同时实现数据冗余备份。 

  多台机器分为两个角色,一个为master,实现写数据,是提供数据的一方,另一个是角色slave,可以有多个,实现读数据,是接受数据的一方。把master中的数据复制到slave中。

  主从复制的原理:当数据库启动后,slave会向master发送SYNC请求。master 会执行rdb持久化的同时会起一个buffer用来存执行rdb期间有客户端发来的命令。快照完成后,master会将rdb文件和buffer中的命令依次发送给slave。后续master再有接受到客户端的写命令,就会将命令同步给slave。

  主从复制的流程:

  1、建立连接。

    slave主动连接master,slave发送指令“slaveof ip port”给目标服务器

    master接受到指令,响应对方

    slave保存master的ip和port

    在slave端创建与master进行通信连接的socket

    slave端周期性的向master发送"ping"命令,master回应“pong”

    如果master设置有密码,slave需要验证

    slave发送命令“replconf listen-port<port>”告诉master的端口号,master接受并保存

建立连接的方式:

 

#方法一:客户端发送命令slaveof <masterip> <masterport> 
slaveof 192.168.31.119 6379
#方法二:客户端启动时
redis-server -slaveof  192.168.31.119 6379
#方法三:修改配置文件
slaveof 192.168.31.119 6379

 

master设置密码和密码校验

#master端设置密码 
requirepass   123456
#slave端验证密码
auth 123456

 手动搭建主从结构

  一共需要,master服务器,master客户端,slave服务器,slave客户端

  第一步:设置两个角色的服务器配置文件

  第二步:分别启动master(端口号:6379)服务器、slave(端口号:6380)服务器

  第三步:slave主动连接master

通过配置文件实现主从复制

在6380的配置文件中添加

 

slaveof 127.0.0.1 6379

 

 

 2、数据同步。在slave初次连接master后,复制master中的所有数据到slave。具体步骤:

    slave发送指令"psync2  runid  offset",请求同步,初始时发送“psync2  ? -1”,因为master的rundi和offset都不知道。

    master执行bgsave,再创建一个命令缓冲区(存放新的指令),记录当前的偏移量offset,生成rdb文件,通过socket通信把文件、runid和offset传递给slave;

    slave清空原始数据,接受rdb,恢复文件,并保存runid和offset;

    (以上三步是完成全量复制)

       salve恢复完成后通知master,发送命令psync2  runid  offset

    master会判断对方的runid匹不匹配,offset是否在缓冲区中,只要有一个不满足就执行全量复制。如果校验通过,但是slaver发送的offset与master保存的offset不一致,master将缓冲区中的指令(可以理解为aof)和offset发送给slave

    slave保存master的offset,执行aof重写,恢复数据。

    (以上部分复制

    数据同步阶段的注意事项:如果master数量很大,数据同步时应该错开高峰。

    当master与slave全量复制的过程中有持续的命令进来,复制缓冲区有限(默认一兆),那么最前面的数据很可能丢失,master与slave进行部分同步时,发现有丢失,就会全量复制。

  3、命令转播。当master数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的状态。实时同步。 进入命令传播阶段候,master与slave间需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线 

  补充部分复制中的几个要素:

    runid:每一台服务器每次运行的身份识别码,一台服务器多次运行可以生成多个运行id ,40位字符组成,是一个随机的十六进制字符 。在第一个阶段,当slave来连master时,就会把自己的runid发送给master,master会保存,如果下次slaver与master再次通信时,master发现slaver的runid变了,master就会意识到数据不一致。

    master的复制积压缓冲区:当master收到一个命令时,master中的一个"命令传播程序"会把命令发送给每一个slaver,如果其中的某一条线路发生故障,该命令没有传播到slaver中,此时数据就不能同步。为了解决这个问题,引入缓冲区,master再发送命令的同时会把命令保存在缓冲区一份。

   master记录已发送的信息对应的offset 

   slave记录已接收的信息对应的offset 

    双方的复制偏移量:

    master复制偏移量:记录发送给所有slave的指令字节对应的位置(多个)

    slave复制偏移量:记录slave接收master发送过来的指令字节对应的位置(一个) 

  

 4、哨兵

  哨兵(sentinel) 是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的 master并将所有slave连接到新的master,哨兵也是一台redis服务器,只是不提供数据服务 通常哨兵配置数量为单数 。

  哨兵的配置文件:

#哨兵的端口
port 26379
#有关哨兵的信息存储目录
dir /tmp
#“mymaster”起的名称,后面ip+port是监听的对象,“2”表示如果有两台哨兵认为master宕机了,就表示真的宕机了
sentinel monitor mymaster 127.0.0.1 6379 2
#指定哨兵在监控Redis服务时,判定服务器挂掉的时间周期,默认30秒 
sentinel down-after-milliseconds mymaster 30000
#新的master一次向多少个slave同步
sentinel parallel-syncs mymaster 1
#同步超时时间
sentinel failover-timeout mymaster 180000

   搭建哨兵。预备搭建一个master,两个slave,三个哨兵

  第一步:设置配置文件

  第二步:搭建主从结构

  第三步:启动三个哨兵

 

  最后一个哨兵启动后打印信息

 

  当我们关闭6379端口的服务器(master),哨兵服务器打印如下信息

 哨兵的工作原理:

  第一阶段:监控阶段,同步各个结点的状态信息

    sentinel会分别向master、slave、其他sentinel索取状态信息。

    向master发送“info”获取mster的runid、role、与master连接的各个slave信息,根据从master上获取的slave信息然后分别向slave再获取信息,获取各个slave的runid、role、offset、master_ip、master_port。各个sentinel之间会搭建一个环,他们在这个环中订阅信息、发布信息、同步信息。

  第二阶段:通知阶段

    几个sentinel形成一个小的群体,他们之前进行信息的同步。而master和slave正常运行,sentinel会发送"hello"获取他们几个的工作状态,其中的一个sentinel拿到这个状态会向其他几个sentinel进行信息互通。

  第三阶段:故障转移阶段

    当其中一台sentinel发现master下线了,会给master标记一个状态“SRI_S_DOWN”,然后将这个信息发送给其他几个sentinel,其他几个也获取master的状态信息,有一半以上的sentinel认为master挂掉了,就会给master标记“SRI_O_DOWN”,证明该master确实下线了。

    内部几个sentinel竞选,选出一个“负责人”,这个“负责人”会根据一定的原则(在线的、响应快的、优先级高的)从slave中选择一个充当master。其他的slave切换master。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM