1. Overview
非常非常的快,有测评说比Memcached还快(当大家都是单CPU的时候),而且是无短板的快,读写都一般的快,所有API都差不多快,也没有MySQL Cluster、MongoDB那样更新同一条记录如Counter时慢下去的毛病。
丰富的数据结构,超越了一般的Key-Value数据库而被认为是一个数据结构服务器。组合各种结构,限制Redis用途的是你自己的想象力,作者自己捉刀写的用途入门
因为是个人作品,Redis目前只有2.3万行代码,Keep it simple的死硬做法,使得普通公司而不需淘宝那个级别的文艺公司也可以吃透它。Redis宣言就是作者的自白,我最喜欢其中的“代码像首诗”,”设计是一场与复杂性的战斗“,“Coding是一件艰苦的事情,唯一的办法是享受它。如果它已不能带来快乐就停止它。为了防止这一天的出现,我们要尽量避免把Redis往乏味的路上带。”
让人又爱又恨的单线程架构,使得代码不用处理平时最让人头痛的并发而大幅简化,但也带来CPU的瓶颈,而且单线程被慢操作所阻塞时,其他请求的延时变得不确定。
那Redis不是什么?
Redis 不是Big Data,数据都在内存中,无法以T为单位。在Redis-Cluster发布并被稳定使用之前,Redis没有真正的平滑水平扩展能力。Redis 不支持Ad-Hoc Query,提供的只是数据结构的API,没有SQL一样的查询能力。最普通的key-value类型,说是String,其实是任意的byte[],比如图片,最大512M。 所有常用命令的复杂度都是O(1),普通的Get/Set方法,可以用来做Cache,存session,为了简化架构甚至可以替换掉Memcached。
Incr/IncrBy/IncrByFloat/Decr/DecrBy,可以用来做计数器,做自增序列。key不存在时会创建并贴心的设原值为0。IncrByFloat专门针对float,没有对应的decrByFloat版本?用负数啊。
SetNx, 仅当key不存在时才Set。可以用来选举Master或做分布式锁:所有Client不断尝试使用SetNx master myName抢注Master,成功的那位不断使用Expire刷新它的过期时间。如果Master倒掉了key就会失效,剩下的节点又会发生新一轮抢夺。
其他Set指令:
SetEx, Set + Expire 的简便写法,p字头版本以毫秒为单位。GetSet, 设置新值,返回旧值。比如一个按小时计算的计数器,可以用GetSet获取计数并重置为0。这种指令在服务端做起来是举手之劳,客户端便方便很多。MGet/MSet/MSetNx, 一次get/set多个key。2.6.12版开始,Set命令已融合了Set/SetNx/SetEx三者,SetNx与SetEx可能会被废弃。GetBit/SetBit/BitOp,与或非/BitCount, BitMap的玩法,比如统计今天的独立访问用户数时,每个注册用户都有一个offset,他今天进来的话就把他那个位设为1,用BitCount就可以得出今天的总人数。
Append/SetRange/GetRange/StrLen,对文本进行扩展、替换、截取和求长度,只对特定数据格式如字段定长的有用,json就没什么用。
Key-HashMap结构,相比String类型将这整个对象持久化成JSON格式,Hash将对象的各个属性存入Map里,可以只读取/更新对象的某些属性。这样有些属性超长就让它一边呆着不动,另外不同的模块可以只更新自己关心的属性而不会互相并发覆盖冲突。
另一个用法是土法建索引。比如User对象,除了id有时还要按name来查询。可以有如下的数据记录:
(String) user:101 -> {“id”:101,”name”:”calvin”…}(String) user:102 -> {“id”:102,”name”:”kevin”…}(Hash) user:index-> “calvin”->101, “kevin” -> 102底层实现是hash table,一般操作复杂度是O(1),要同时操作多个field时就是O(N),N是field的数量。
List是一个双向链表,支持双向的Pop/Push,江湖规矩一般从左端Push,右端Pop——LPush/RPop,而且还有Blocking的版本BLPop/BRPop,客户端可以阻塞在那直到有消息到来,所有操作都是O(1)的好孩子,可以当Message Queue来用。当多个Client并发阻塞等待,有消息入列时谁先被阻塞谁先被服务。任务队列系统Resque是其典型应用。
还有RPopLPush/ BRPopLPush,弹出来返回给client的同时,把自己又推入另一个list,LLen获取列表的长度。
还有按值进行的操作:LRem(按值删除元素)、LInsert(插在某个值的元素的前后),复杂度是O(N),N是List长度,因为List的值不唯一,所以要遍历全部元素,而Set只要O(log(N))。
按下标进行的操作:下标从0开始,队列从左到右算,下标为负数时则从右到左。
LSet ,按下标设置元素值。LIndex,按下标返回元素。LRange,不同于POP直接弹走元素,只是返回列表内一段下标的元素,是分页的最爱。LTrim,限制List的大小,比如只保留最新的20条消息。复杂度也是O(N),其中LSet的N是List长度,LIndex的N是下标的值,LRange的N是start的值+列出元素的个数,因为是链表而不是数组,所以按下标访问其实要遍历链表,除非下标正好是队头和队尾。LTrim的N是移除元素的个数。
在消息队列中,并没有JMS的ack机制,如果消费者把job给Pop走了又没处理完就死机了怎么办?
解决方法之一是加多一个sorted set,分发的时候同时发到list与sorted set,以分发时间为score,用户把job做完了之后要用ZREM消掉sorted set里的job,并且定时从sorted set中取出超时没有完成的任务,重新放回list。另一个做法是为每个worker多加一个的list,弹出任务时改用RPopLPush,将job同时放到worker自己的list中,完成时用LREM消掉。如果集群管理(如zookeeper)发现worker已经挂掉,就将worker的list内容重新放回主list。Set就是Set,可以将重复的元素随便放入而Set会自动去重,底层实现也是hash table。
SAdd/SRem/SIsMember/SCard/SMove/SMembers,各种标准操作。除了SMembers都是O(1)。SInter/SInterStore/SUnion/SUnionStore/SDiff/SDiffStore,各种集合操作。交集运算可以用来显示在线好友(在线用户 交集 好友列表),共同关注(两个用户的关注列表的交集)。O(N),并集和差集的N是集合大小之和,交集的N是小的那个集合的大小*2。有序集,元素放入集合时还要提供该元素的分数。
ZRange/ZRevRange,按排名的上下限返回元素,正数与倒数。ZRangeByScore/ZRevRangeByScore,按分数的上下限返回元素,正数与倒数。ZRemRangeByRank/ZRemRangeByScore,按排名/按分数的上下限删除元素。ZCount,统计分数上下限之间的元素个数。ZRank/ZRevRank ,显示某个元素的正倒序的排名。ZScore/ZIncrby,显示元素的分数/增加元素的分数。ZAdd(Add)/ZRem(Remove)/ZCard(Count),ZInsertStore(交集)/ZUnionStore(并集),Set操作,与正牌Set相比,少了IsMember和差集运算。Sorted Set的实现是hash table(element->score, 用于实现ZScore及判断element是否在集合内),和skip list(score->element,按score排序)的混合体。 skip list有点像平衡二叉树那样,不同范围的score被分成一层一层,每层是一个按score排序的链表。
ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是结果/操作元素的个数。可见,原本可能很大的N被很关键的Log了一下,1000万大小的Set,复杂度也只是几十不到。当然,如果一次命中很多元素M很大那谁也没办法了。
用Multi(Start Transaction)、Exec(Commit)、Discard(Rollback)实现。 在事务提交前,不会执行任何指令,只会把它们存到一个队列里,不影响其他客户端的操作。在事务提交时,批量执行所有指令。《Redis设计与实现》中的详述。
注意,Redis里的事务,与我们平时的事务概念很不一样:
它仅仅是保证事务里的操作会被连续独占的执行。因为是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的。它没有隔离级别的概念,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题。它不保证原子性——所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力。在redis里失败分两种,一种是明显的指令错误,比如指令名拼错,指令参数个数不对,在2.6版中全部指令都不会执行。另一种是隐含的,比如在事务里,第一句是SET foo bar, 第二句是LLEN foo,对第一句产生的String类型的key执行LLEN会失败,但这种错误只有在指令运行后才能发现,这时候第一句成功,第二句失败。还有,如果事务执行到一半redis被KILL,已经执行的指令同样也不会被回滚。Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行。
Redis2.6内置的Lua Script支持,可以在Redis的Server端一次过运行大量逻辑,就像存储过程一样,避免了海量中间数据在网路上的传输。
Lua自称是在Script语言里关于快的标准,Redis选择了它而不是流行的javaScript。因为Redis的单线程架构,整个Script默认是在一个事务里的。Script里涉及的所有Key尽量用变量,从外面传入,使Redis一开始就知道你要改变哪些key。(but why?)Eval每次传输一整段Script比较费带宽,可以先用Script Load载入script,返回哈希值。然后用EvalHash执行。因为就是SHA-1,所以任何时候执行返回的哈希值都是一样的。内置的Lua库里还很贴心的带了CJSON,可以处理json字符串。一段用Redis做Timer的示例代码,下面的script被定期调用,从以触发时间为score的sorted set中取出已到期的Job,放到list中给Client们blocking popup。12345678910111213141516 | -- KEYS: [1]job:sleeping, [2]job:ready-- ARGS: [1]currentTime-- Comments: result is the job idlocal jobs=redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1])local count = table.maxn(jobs) if count>0 then -- Comments: remove from Sleeping Job sorted set redis.call('zremrangebyscore', KEYS[1], '-inf', ARGV[1]) -- Comments: add to the Ready Job list -- Comments: can optimize to use lpush id1,id2,... for better performance for i=1,count do redis.call('lpush', KEYS[2], jobs[i]) endend |
1 |
官方文档 与 《Redis设计与实现》中的详述,过期数据的清除从来不容易,为每一条key设置一个timer,到点立刻删除的消耗太大,每秒遍历所有数据消耗也大,Redis使用了一种相对务实的做法: 当client主动访问key会先对key进行超时判断,过时的key会立刻删除。 如果clien永远都不再get那条key呢? 它会在Master的后台,每秒10次的执行如下操作: 随机选取100个key校验是否过期,如果有25个以上的key过期了,立刻额外随机选取下100个key(不计算在10次之内)。可见,如果过期的key不多,它最多每秒回收200条左右,如果有超过25%的key过期了,它就会做得更多,但只要key不被主动get,它占用的内存什么时候最终被清理掉只有天知道。
数据准备: 预加载两千万条数据,占用10G内存。
测试工具:自带的redis-benchmark,默认只是基于一个很小的数据集进行测试,调整命令行参数如下,就可以开100条线程(默认50),SET 1千万次(key在0-1千万间随机),key长21字节,value长256字节的数据。
1 | redis-benchmark-tSET-c100-n10000000-r10000000-d256 |
Redis-Cluster是今年工作重点,支持automatic re-sharding, 采用和Hazelcast类似的算法,总共有N个分区(eg.N=1024),每台Server负责若干个分区。
在客户端先hash出key 属于哪个分区,随便发给一台server,server会告诉它真正哪个Server负责这个分区,缓存下来,下次还有该分区的请求就直接发到地儿了。Re-sharding时,会将某些分区的数据移到新的Server上,完成后各Server周知分区<->Server映射的变化,因为分区数量有限,所以通讯量不大。 在迁移过程中,客户端缓存的依然是旧的分区映射信息,原server对于已经迁移走的数据的get请求,会返回一个临时转向的应答,客户端先不会更新Cache。等迁移完成了,就会像前面那样返回一条永久转向信息,客户端更新Cache,以后就都去新server了。高可用性关乎系统出错时到底会丢失多少数据,多久不能服务。要综合考虑持久化,Master-Slave复制及Fail-Over配置,以及具体Crash情形,比如Master死了,但Slave没死。或者只是Redis死了,操作系统没死等等。
综上所述,RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值,比如之前的benchmark每个小时会产生40G大小的AOF文件,如果硬盘能撑到半夜系统闲时才用cron调度bgaofrewrite就好了。
如果不Enable AOF ,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构,见Tim的博客
现象描述:当AOF rewrite 15G大小的内存时,Redis整个死掉的样子,所有指令甚至包括slave发到master的ping,redis-cli info都不能被执行。
原因分析:
官方文档,由IO产生的Latency详细分析, 已经预言了悲剧的发生,但一开始没留意。Redis为求简单,采用了单请求处理线程结构。打开AOF持久化功能后, Redis处理完每个事件后会调用write(2)将变化写入kernel的buffer,如果此时write(2)被阻塞,Redis就不能处理下一个事件。Linux规定执行write(2)时,如果对同一个文件正在执行fdatasync(2)将kernel buffer写入物理磁盘,或者有system wide sync在执行,write(2)会被block住,整个Redis被block住。如果系统IO繁忙,比如有别的应用在写盘,或者Redis自己在AOF rewrite或RDB snapshot(虽然此时写入的是另一个临时文件,虽然各自都在连续写,但两个文件间的切换使得磁盘磁头的寻道时间加长),就可能导致fdatasync(2)迟迟未能完成从而block住write(2),block住整个Redis。为了更清晰的看到fdatasync(2)的执行时长,可以使用”strace -p (pid of redis server) -T -e -f trace=fdatasync”,但会影响系统性能。Redis提供了一个自救的方式,当发现文件有在执行fdatasync(2)时,就先不调用write(2),只存在cache里,免得被block。但如果已经超过两秒都还是这个样子,则会硬着头皮执行write(2),即使redis会被block住。此时那句要命的log会打印:“Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.” 之后用redis-cli INFO可以看到aof_delayed_fsync的值被加1。因此,对于fsync设为everysec时丢失数据的可能性的最严谨说法是:如果有fdatasync在长时间的执行,此时redis意外关闭会造成文件里不多于两秒的数据丢失。如果fdatasync运行正常,redis意外关闭没有影响,只有当操作系统crash时才会造成少于1秒的数据丢失。解决方法:最后发现,原来是AOF rewrite时一直埋头的调用write(2),由系统自己去触发sync。在RedHat Enterprise 6里,默认配置vm.dirty_background_ratio=10,也就是占用了10%的可用内存才会开始后台flush,而我的服务器有64G内存。很明显一次flush太多数据会造成阻塞,所以最后果断设置了sysctl vm.dirty_bytes=33554432(32M),问题解决。
然后提了个issue,AOF rewrite时定时也执行一下fdatasync嘛, antirez三分钟后就回复了,新版中,AOF rewrite时32M就会重写主动调用fdatasync。
有时候明明master/slave都活得好好的,突然间就说要重新进行全同步了:
1.Slave显示:# MASTER time out: no data nor PING received…
slave会每隔repl-ping-slave-period(默认10秒)ping一次master,如果超过repl-timeout(默认60秒)都没有收到响应,就会认为Master挂了。如果Master明明没挂但被阻塞住了也会报这个错。可以适当调大repl-timeout。
2.Master显示:# Client addr=10.175.162.123:44670 flags=S oll=104654 omem=2147487792 events=rw cmd=sync scheduled to be closed ASAP for overcoming of output buffer limits.
当slave没挂但被阻塞住了,比如正在loading Master发过来的RDB, Master的指令不能立刻发送给slave,就会放在output buffer中(见oll是命令数量,omem是大小),在配置文件中有如下配置:client-output-buffer-limit slave 256mb 64mb 60, 这是说负责发数据给slave的client,如果buffer超过256m或者连续60秒超过64m,就会被立刻强行关闭!!! Traffic大的话一定要设大一点。否则就会出现一个很悲剧的循环,Master传输一个大的RDB给Slave,Slave努力的装载,但还没装载完,Master对client的缓存满了,再来一次。
平时可以在master执行 redis-cli client list 找那个cmd=sync,flag=S的client,注意OMem的变化。
Redis-sentinel是2.6版开始加入的另一组独立运行的节点,提供自动Fail Over的支持。
官方文档 与 Redis核心解读–集群管理工具(Redis-sentinel)antirez 对 Sentinel的反驳,与下篇master地址在sentinel.conf里, sentinel会每10秒一次向master发送INFO,知道master的slave有哪些。 如果master已经变为slave,sentinel会分析INFO的应答指向新的master。以前,sentinel重启时,如果master已经切换过了,但sentinel.conf里master的地址并没有变,很可能有悲剧发生。另外master重启后如果没有切换成slave,也可能有悲剧发生。新版好像修复了一点这个问题,待研究。
另外,sentinel会在master上建一个pub/sub channel,名为”sentinel:hello”,通告各种信息,sentinel们也是通过接收pub/sub channel上的+sentinel的信息发现彼此,因为每台sentinel每5秒会发送一次自己的host信息,宣告自己的存在。
觉得Sentinel至少有两个可提升的地方:
一是如果master 主动shutdown,比如系统升级,有办法主动通知sentinel提升新的master,减少服务中断时间。二是比起redis-server太原始了,要自己丑陋的以nohup sentinel > logfile 2>&1 & 启动,也不支持shutdown命令,要自己kill pid。基于Sentinel的方案,client需要执行语句SENTINEL get-master-addr-by-name mymaster 可获得当前master的地址。 Jedis正在集成sentinel,已经支持了sentinel的一些指令,但还没发布,但sentinel版的连接池则暂时完全没有,在公司的项目里我参考网友的项目自己写了一个。
淘宝的Tedis driver,使用了完全不同的思路,不基于Sentinel,而是多写随机读, 一开始就同步写入到所有节点,读的话随便读一个还活着的节点就行了。但有些节点成功有些节点失败如何处理? 节点死掉重新起来后怎么重新同步?什么时候可以重新Ready? 所以不是很敢用。
另外如Ruby写的redis_failover,也是抛开了Redis Sentinel,基于ZooKeeper的临时方案。
Redis作者也在博客里抱怨怎么没有人做Dynamo-style 的client。
约30个配置项,全都有默认配置,对redif.conf默认配置的修改见附录1。
综述: Redis监控技巧
Info指令将返回非常丰富的信息。 着重监控检查内存使用,是否已接近上限,used_memory是Redis申请的内存,used_memory_rss是操作系统分配给Redis的物理内存,两者之间隔着碎片,隔着Swap。 还有重点监控 AOF与RDB文件的保存情况,以及master-slave的关系。Statistic 信息还包括key命中率,所有命令的执行次数,所有client连接数量等, CONFIG RESETSTAT 可重置为0。
Monitor指令可以显示Server收到的所有指令,主要用于debug,影响性能,生产环境慎用。
SlowLog 检查慢操作(见2.性能)。
官网列出了如下工具,但暂时没发现会直接拿来用的:
Redis Live,基于Python的web应用,使用Info和Monitor获得系统情况和指令统计分析。 因为Monitor指令影响性能,所以建议用cron定期运行,每次偷偷采样两分钟的样子。phpRedisAdmin,基于php的Web应用,目标是MysqlAdmin那样的管理工具,可以管理每一条Key的情况,但它的界面应该只适用于Key的数量不太多的情况,Demo。Redis Faina,基于Python的命令行,Instagram出品,用户自行获得Monitor的输出后发给它进行统计分析。由于Monitor输出的格式在Redis版本间不一样,要去github下最新版。Redis-rdb-tools 基于Python的命令行,可以分析RDB文件每条Key对应value所占的大小,还可以将RDB dump成普通文本文件然后比较两个库是否一致,还可以将RDB输出成JSON格式,可能是最有用的一个了。Redis Sampler,基于Ruby的命令行,antirez自己写的,统计数据分布情况。各个Driver好像只有Jedis比较活跃,但也5个月没提交了,也是Java里唯一的Redis官方推荐。
Spring Data Redis的封装并不太必要,因为Jedis已足够简单,没有像Spring Data MongoDB对MongoDB java driver的封装那样大幅简化代码,顶多就是加强了一点点点pipeline和transaction状态下的coding,禁止了一些此状态下不能用的命令。而所谓屏蔽各种底层driver的差异并不太吸引人,因为我就没打算选其他几种driver。有兴趣的可以翻翻它的JedisConnection代码。
所以,SpringSide直接在Jedis的基础上,按Spring的风格封装了一个JedisTemplate,负责从池中获取与归还Jedis实例,处理异常。
Jedis基于Apache Commons Pool做的连接池,默认MaxActive最大连接数只有8,必须重新设置。而且MaxIdle也要相应增大,否则所有新建的连接用完即弃,然后会不停的重新连接。
另外Jedis设定了每30秒对所有连接执行一次ping,以发现失效的连接,这样每30秒会有一个拿不到连接的高峰。但效果如何需要独立分析。比如系统高峰之后可能有一长段时间很闲,而且Redis Server那边做了Timeout控制会把连接断掉,这时候做idle checking是有意义的,但30秒一次也太过频繁了。否则关掉它更好。
Jedis的blocking pop函数,应用执行ExecutorService.shutdownNow()中断线程时并不能把它中断,见讨论组。两个解决方法:
不要用不限时的blocking popup,传多一个超时时间参数,如5秒。找地方将调用blocking popup的jedis保存起来,shutdown时主动调用它的close。Windows版本方便对应用的本地开发调试,但Redis并没有提供,好在微软提供了一个依赖LibUV实现兼容的补丁,https://github.com/MSOpenTech/redis,但redis作者拒绝合并到master中,微软只好苦憋的时时人工同步。 目前的稳定版是2.6版本,支持Lua脚本。
因为github现在已经没有Download服务了,所以编译好的可执行文件藏在这里:
https://github.com/MSOpenTech/redis/tree/2.6/bin/release注:下文中的链接都是网站的架构描述文档。
Twitter和新浪微博, 都属于将Redis各种数据结构用得出神入化的那种,如何发布大V如奥巴马的消息是它们最头痛的问题。
Tumblr: 11亿美刀卖给Yahoo的图片日志网站,22 台Redis server,每台运行8 – 32个实例,总共100多个Redis实例在跑。有着Redis has been completely problem free and the community is great的崇高评价。Redis在里面扮演了八爪鱼多面手的角色:
Dashboard的海量通知的存储。Dashboard的二级索引。存储海量短链接的HBase前面的缓存。Gearman Job Queue的存储。正在替换另外30台memcached。Instagram ,曾经,Redis powers their main feed, activity feed, sessions system, and other services。但可惜目前已迁往Cassandra,说新架构只需1/4的硬件费用,是的,就是那个导致Digg CTO辞职的Canssandra。
Flickr , 依然是asynchronous task system and rudimentary queueing system。之前Task system放在mysql innodb,根本,撑不住。
The Others:
Pinterest,混合使用MySQL、Membase与Redis作为存储。Youporn.com,100%的Redis,MySQL只用于创建新需求用到的sorted set,300K QPS的大压力。日本微信 ,Redis在前负责异步Job Queue和O(n)的数据,且作为O(n*t)数据的cache,HBase在后,负责O(n*t)数据, n是用户,t是时间。StackOverflow ,2 Redis servers for distribute caching,好穷好轻量。Github,任务系统Resque的存储。Discourge,号称是为下一个十年打造的论坛系统, We use Redis for our job queue, rate limiting, as a cache and for transient data,刚好和我司的用法一样。extension modules项目封装了常用的函数与场景,showcase example的src/demo/redis目录里有各场景的benchmark测试。
典型的Spring Template风格,和JdbcTemplate,HibernateTemplate一样,封装从JedisPool获取与归还Connecton的代码,有带返回值与无返回值两种返回接口。同时,对最常用的Jedis调用,直接封装了一系列方法。
Scheduler实现了基于Redis的高并发单次定时任务分发。具体选型见Scheduler章节。
Master Elector基于redis setNx()与expire()两个api实现,与基于Zookeeper,Hazelcast实现的效果类似。
计有Session,Counter,Scheduler 与 Master Elector四款。
新闻热点
疑难解答