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。