twitter的 twemproxy 是一个Redis的代理服务程序,能够实现key的分片。分片能使key均匀地分布到集群的机器上去,能保证数据的一致性,有着众多的优点。
但从Redis单实例切换到twemproxy集群时,还是有些需要注意的地方:
不支持的方法:
KEYS,MIGRATE,SCAN等
支持但需特殊处理的方法:
MSET,SINTERSTORE,SUNIONSTORE,ZINTERSTORE,ZUNIONSTORE等
全部请查看 Redis命令列表
对于不支持的方法,在使用时需要寻找替代方案。本文主要解决一下需特殊处理的方法。
单实例上的MSET是一个原子性(atomic) *** 作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key 没有改变的情况,不可能发生。
而集群上虽然也支持同时设置多个key,但不再是原子性 *** 作。会存在某些给定 key 被更新而另外一些给定 key 没有改变的情况。其原因是需要设置的多个key可能分配到不同的机器上。
这四个命令属于同一类型。它们的共同之处是都需要对一组key进行运算或 *** 作,但要求这些key都被分配到相同机器上。
这就是分片技术的矛盾之处:
即要求key尽可能地分散到不同机器,又要求某些相关联的key分配到相同机器。
解铃还需系铃人。解决方法还是从分片技术的原理上找。
分片,就是一个hash的过程:对key做md5,sha1等hash算法,根据hash值分配到不同的机器上。
为了实现将key分到相同机器,就需要相同的hash值,即相同的key(改变hash算法也行,但不简单)。
但key相同是不现实的,因为key都有不同的用途。例如user:user1:ids保存用户的tweets ID,user:user1:tweets保存tweet的具体内容,两个key不可能同名。
仔细观察user:user1:ids和user:user1:tweets,两个key其实有相同的地方,即user1。能不能拿这一部分去计算hash呢?
这就是 Hash Tag 。允许用key的部分字符串来计算hash。
当一个key包含 {} 的时候,就不对整个key做hash,而仅对 {} 包括的字符串做hash。
假设hash算法为sha1。对user:{user1}:ids和user:{user1}:tweets,其hash值都等同于sha1(user1)。
Hash Tag是用于hash的部分字符串开始和结束的标记,例如"{}"、"$$"等。
配置时,只需更改hash_tag字段即可
博客链接: >
相信各位在使用redis集群的时候,对于redis集群中的批量 *** 作都会有一个现象:使用redis集群进行批量获取数据的时候,效率总是不高,取一次数据要达到几百毫秒,当你 *** 作的数据是百万级别的时候,你就会发现redis的读取效率压根就不能接受。接下来告诉大家如何进行了解
### redis集群的哈希槽
redis集群中内置了16384个哈希槽,当一个key值准备存储的时候,是先通过将key进行 crc16 校验,校验后的值对16384取值,得到的值就是该key所在的槽(slot);redis集群中,节点上的槽是连续的一段,因此通过我们计算key得到的slot,就能判断该key是在存储在哪个节点上的。
#### 如何判断redis集群中各个节点上的slot分布?
使用命令 : cluster nodes 或者 cluster slots
#### 如何知道一个key值对应的槽
使用命令: cluster keyslots {key}
#### 提高效率的解决方案
因此,通过上面我们就可以知道key值存储对应的reids集群的节点,因此我们可以做以下处理:将你所需要的key按照槽的值进行分批,用单点连接的形式连接到某个redis节点上,批量取处于同一个节点上的key。
注意:
- 一定要用单点的形式进行连接,还是使用集群方式连接的话,就算是处于一个节点,效率也是没有提高的;
- redis集群单点连接的话,不能使用mget,因此mget只能取位于同一个 slot 上的,你可以使用pipeline进行事务处理;
### 一次具体的实现
目前我使用的语言的php,借鉴了
[crc16算法计算](>
通过sentinel来获取redis的可用master ip来创建连接池连接,这里有一个很坑的地方,若sentinel和redis node部署在同一台服务器,sentinel监控的master ip不能写成127001,需要写成真实IP。
sentinel在对外管理MASTER IP时,只是简单的将配置文件中的IP保存,而不会在对外提供服务时,动态转换为127001所在机器的真实IP。
当同时满足以下条件时,使用ziplist编码:
SpringBoot—实现n秒内出现x个异常报警
思路:
借助Redis的zSet集合,score存储的是异常时的时间戳,获取一定时间范围内的set集合。判断set个数是否满足条件,若满足条件则触发报警;
注意点:
相关API:
Redis实现延迟队列方法介绍
基于Redis实现DelayQueue延迟队列设计方案
相关API:
SpringBoot2x—使用Redis的bitmap实现布隆过滤器(Guava中BF算法)
布隆过滤器: 是专门用来检测集合中是否存在特定元素的数据结构。
存在误差率: 即将不在集合的元素误判在集合中。
所以布隆过滤器适合查询准确度要求没这么苛刻,但是对时间、空间效率比较高的场景。
实现方式:Redis实现布隆过滤器——借鉴Guava的BF算法:
SpringBoot2x中使用Redis的bitmap结构(工具类)
注意:bitmap使用存在风险,若仅仅计算hash值,会导致bitmap占用空间过大。一般需要对hash值进行取余处理。
根据Redis是否存在key,判断锁是否被获取;
锁应该是一个对象,记录持有锁的线程信息、当前重入次数。所以应该使用Redis的Hash结构来存储锁对象。
31 网络波动造成释放锁失败怎么解决?
需要为锁加上超时时间;
32 任务未执行完毕时,锁由于超时时间被释放?
线程一旦加锁成功,可以启动一个后台线程,每隔多少秒检查一次,如果线程还持有锁,可以不断延长锁的生存时间。
主从切换时,从服务器上没有加锁信息,导致多个客户端同时加锁。
list结构底层是ziplist/quicklist(可看着一个双端队列)。常用命令:
使用list作为对象的缓存池。通过rpush放入对象,通过lpop取出对象。
若是阻塞取,可以使用blpop命令实现。
Redis和Lua脚本(实现令牌桶限流)
数据结构选择hash。
hash里面维护:最后放入令牌时间、当前桶内令牌量、桶内最大数量、令牌放置速度(元数据)。
被动式维护:
命令:incr原子累加;
对一段固定时间窗口内的请求进行计数,如果请求数超过了阈值,则舍弃该请求;如果没有达到设定的阈值,则接受该请求,且计数加1。当窗口时间结束,重置计数器为0。
优点:实现简单,容易理解;
缺点:流量曲线可能不够平滑,有“突刺现象”。
1 一段时间内(不超过时间窗口)系统服务不可用。 比如窗口大小1s,限流为100,恰好某个窗口第1ms来了100个请求,然后2ms-999ms请求都会被拒绝。这段时间用户会感觉系统服务不可用(即不够平滑)。
2 窗口切换时可能会出现两倍于阈值流量的请求。 比如窗口大小1s,限流大小100,然后在某个窗口的第999ms有100个请求,窗口前期没有请求。所以这100个请求都会通过。然后下一个窗口的第1ms又来100个请求,然后全部通过。其实也是1ms内通过的200个请求。
命令:Redis的incr命令
是对固定窗口计数器的优化,解决的是切换窗口两倍阈值流量的场景。
具体解决方案是:将限流窗口分为多个小的限流窗口,各个限流窗口分别计数。当前时间大于窗口最大时间时,将头部的小窗口数据舍弃,尾部新增小窗口来处理新请求。
优点:本质上是对固定窗口的优化
以上就是关于Redis技巧:分片技术和Hash Tag全部的内容,包括:Redis技巧:分片技术和Hash Tag、redishash命令返回字段值的长度、我是如何解决redis集群批量获取的效率问题的等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)