布隆过滤器是一个很长的二进制数组 + 一系列随机hash算法映射函数,主要用于判断一个元素是否在集合中。能高效的插入和查询,占用空间少,返回的结果是不确定性的。**一个元素如果判断结果为存在的时候元素不一定存在,但是判断结果为不存在时则一定不存在。**布隆过滤器可以添加元素但是不能删除元素,因为删掉元素会导致误判率增加。误判只会发生在过滤器没有添加过的元素,对于添加过的元素不会发生误判。没错就是使用Redis的Bitmap数据类型。
在添加key时:使用多个hash函数对key进行hash运算得到一个整数索引值,对位数组长度进行取模运算得到一个位置,每个hash函数会得到一个不同的位置,将这几个位置都置1就完成了add操作。
查询key时:向布隆过滤器查询某个key是否存在时,先把这个key通过相同的多个hash函数进行运算,查看对应的位置是否都为1,只要有一个位置是0,则表明不存在,如果这几个位置都为1,则说明可能存在。(因为这些位置的1可能是其他的key导致的)
使用布隆过滤器时,最好不要让实际元素数量远大于初始化数量。
当实际元素数量超过初始化数量时,应该对布隆过滤器进行重建,重新分配一个size更大的过滤器,再将所有的历史元素批量add进行。
布隆过滤器是不能删除数据的,因为无法保证hash之后的位置没有别的key在使用(难免会出现hash冲突,多个key共用某些hash位置)。如果删除了,那么别的key也会受影响。这样会导致误判率增加。
1、主要是解决缓存穿透问题
把已存在的数据的key存在布隆过滤器中,相当于在redis前面挡了一层布隆过滤器。
当有新的请求时,先到布隆过滤器中查询是否已经存在;
如果布隆过滤器中不存在该数据则直接返回;
如果布隆过滤器中已存在,才去查询缓存redis,如果redis里没有查到则到MySQL数据库中查询。
2、黑名单校验
服务器配置有两个和慢查询日志相关的选项:
slowlog-log-slower-than选项指定执行时间超过多少微秒(1秒等于1 000 000微秒)的命令请求会被记录到日志上。举个例子,如果这个选项的值为100,那么执行时间超过100微秒的命令就会被记录到慢查询日志;如果这个选项的值为500,那么执行时间超过500微秒的命令就会被记录到慢查询日志。
slowlog-max-len选项指定服务器最多保存多少条慢查询日志。服务器使用先进先出的方式保存多条慢查询日志,当服务器存储的慢查询日志数量等于slowlog-max-len选项的值时,服务器在添加一条新的慢查询日志之前,会先将最旧的一条慢查询日志删除。举个例子,如果服务器slowlog-max-len的值为100,并且假设服务器已经储存了100条慢查询日志,那么如果服务器打算添加一条新日志的话,它就必须先删除目前保存的最旧的那条日志,然后再添加新日志。
最佳实践
slowlog-max-len配置建议:线上建议调大慢查询列表,记录慢查询时,Redis会对长命令做截断操作,并不会占用大量命令。可以设置为1000以上。
slowlog-log-slower-than配置建议:默认值超过10毫秒判断为慢查询,需要根据Redis并发量调整该值,对于高流量场景,如果命令执行时间在1毫秒以上,那么Redis最多可支撑OPS不到1000,因此对于高OPS场景,建议设置为1毫秒。
慢查询只记录命令执行时间,并不包括命令排队和网络传输时间。因此客户端执行命令的时间会大于实际命令执行时间。因为命令执行排队机制,慢查询会导致其他命令级联阻塞,因此客户端出现请求超时,需要检查该时间点是都有对应的慢查询,从而分析出是否为慢查询导致的命令级联阻塞。
可能会丢失部分慢查询命令数据。为了防止其丢失,可以定期执行slow get命令将慢查询日志持久化到其他存储中,然后制作可视化界面进行查询。
Redis通过MULTI、EXEC、WATCH等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
一个事务从开始到结束通常会经历以下三个阶段:1)事务开始。2)命令入队。3)事务执行。
事务开始
MULTI命令的执行标志着事务的开始,执行该命令的客户端从非事务状态切换至事务状态。
命令入队
当一个客户端处于非事务状态时,这个客户端发送的命令会立即被服务器执行。当一个客户端切换到事务状态之后,服务器会根据这个客户端发来的不同命令执行不同的操作:如果客户端发送的命令为EXEC、DISCARD、WATCH、MULTI四个命令的其中一个,那么服务器立即执行这个命令。与此相反,如果客户端发送的命令是这四个命令以外的其他命令,那么服务器并不立即执行这个命令,而是将这个命令放入一个事务队列里面(事务队列以先进先出的方式保存入队的命令,较先入队的命令会被放到数组的前面,而较后入队的命令则会被放到数组的后面),然后向客户端返回QUEUED回复。
事务执行
当一个处于事务状态的客户端向服务器发送EXEC命令时,这个EXEC命令将立即被服务器执行。服务器会遍历这个客户端的事务队列,执行队列中保存的所有命令,最后将执行命令所得的结果全部返回给客户端。
一个Redis集群通常由多个节点(node)组成,在刚开始的时候,每个节点都是相互独立的,它们都处于一个只包含自己的集群当中,要组建一个真正可工作的集群,必须将各个独立的节点连接起来,构成一个包含多个节点的集群。连接各个节点的工作可以使用CLUSTER MEET命令来完成(CLUSTER MEET <ip> <port>
),向一个节点node发送CLUSTER MEET命令,可以让node节点与ip和port所指定的节点进行握手(handshake),当握手成功时,node节点就会将ip和port所指定的节点添加到node节点当前所在的集群中。
Redis集群没有使用一致性hash算法的,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置到哪个槽,每个集群的每个节点负责一部分hash槽。CRC16算法产生的hash值有16bit,也就是2^16=65535个值,为什么选择mod 16384而不是mod 65535?
为什么redis集群的最大槽数是16384个?
1、消息大小考虑:尽管crc16能得到65535个值,但redis选择16384个slot,是因为在Redis集群中,发送心跳消息的消息头,16384的消息只占用了2k,而65535则需要8k,消息头过于庞大,浪费带宽。
2、集群规模设计考虑:集群设计最多支持1000个分片,16384是相对比较好的选择,需要保证在最大集群规模下,slot均匀分布场景下,每个分片平均分到的slot不至于太小。
3、槽位越小,节点越少的情况下,压缩比高,容易传输:Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中会对bitmap进行压缩,但是如果bitmap的填充率slots/N很大的话(N表示节点数),bitmap的压缩率就很低,如果节点数很少的,而哈希槽的数量很多,bitmap的压缩率就很低。
Sentinel(哨岗、哨兵)是Redis的高可用性(highavailability)解决方案:由一个或多个Sentinel实例(instance)组成的Sentinel系统(system)可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作。
Redis选举领头Sentinel的规则和方法
所有在线的Sentinel都有被选为领头Sentinel的资格,换句话说,监视同一个主服务器的多个在线Sentinel中的任意一个都有可能成为领头Sentinel。
每次进行领头Sentinel选举之后,不论选举是否成功,所有Sentinel的配置纪元(configuration epoch)的值都会自增一次。配置纪元实际上就是一个计数器,并没有什么特别的。
在一个配置纪元里面,所有Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会,并且局部领头一旦设置,在这个配置纪元里面就不能再更改。
每个发现主服务器进入客观下线的Sentinel都会要求其他Sentinel将自己设置为局部领头Sentinel。
当一个Sentinel(源Sentinel)向另一个Sentinel(目标Sentinel)发送SENTINEL is-master-down-by-addr命令,并且命令中的runid参数不是*符号而是源Sentinel的运行ID时,这表示源Sentinel要求目标Sentinel将前者设置为后者的局部领头Sentinel。