1. Overview
1.1 资料
-
<The Little Redis Book> ,最好的入门小册子,可以先于一切文档之前看,免费。
-
Redis 命令中文版, huangz同学的翻译。
-
Redis设计与实现 ,又是huangz同学的巨作,深入了解内部实现机制。
-
Redis 2.6源码中文注释版 ,继续是huangz同学的大功德。
-
《Redis in Action》 (Manning, 2013) MEAP版,看目录挺实战,亚马逊中国预售250元人民币。
1.2 优缺点
-
Redis 不是Big Data,数据都在内存中,无法以T为单位。
-
在Redis-Cluster发布并被稳定使用之前,Redis没有真正的平滑水平扩展能力。
-
Redis 不支持Ad-Hoc Query,提供的只是数据结构的API,没有SQL一样的查询能力。
1.3 Feature速览
-
所有数据都在内存中。
-
五种数据结构:String / Hash / List / Set / Ordered Set。
-
数据过期时间支持。
-
不完全的事务支持。
-
服务端脚本:使用Lua Script编写,类似存储过程的作用。
-
PubSub:捞过界的消息一对多发布订阅功能,起码Redis-Sentinel使用了它。
-
持久化:支持定期导出内存的Snapshot 与 记录写操作日志的Append Only File两种模式。
-
Replication:Master-Slave模式,Master可连接多个只读Slave,暂无专门的Geographic Replication支持。
-
Fail-Over:Redis-Sentinel节点负责监控Master节点,在master失效时提升slave,独立的仲裁节点模式有效防止脑裂。
-
Sharding:开发中的Redis-Cluser。
-
动态配置:所有参数可用命令行动态配置不需重启,并重新写回配置文件中,对云上的大规模部署非常合适。
1.4 八卦
-
作者是意大利的Salvatore Sanfilippo(antirez),又是VMWare大善人聘请了他专心写Redis。
-
antirez和我一样不喜欢搞什么咨询服务,不过最近VMWare旗下的Pivotal公司开始招聘Redis Commericial Engineer。
-
默认端口6379,是手机按键上MERZ对应的号码,意大利歌女Alessia Merz是antirez和朋友们认为愚蠢的代名词。
数据结构
2.1 Key
-
Key 不能太长,比如1024字节,但antirez也不喜欢太短如”u:1000:pwd”,要表达清楚意思才好。他私人建议用”:”分隔域,用”.”作为单词间的连接,如”comment:1234:reply.to”。
-
Keys,返回匹配的key,支持通配符如 “keys a*” 、 “keys a?c”,但不建议在生产环境大数据量下使用。
-
Sort,对集合按数字或字母顺序排序后返回或另存为list,还可以关联到外部key等。因为复杂度是最高的O(N+M*log(M))(N是集合大小,M 为返回元素的数量),有时会安排到slave上执行。
-
Expire/ExpireAt/Persist/TTL,关于Key超时的操作。默认以秒为单位,也有p字头的以毫秒为单位的版本, Redis的内部实现见2.9 过期数据清除。
2.2 String
-
SetEx, Set + Expire 的简便写法,p字头版本以毫秒为单位。
-
GetSet, 设置新值,返回旧值。比如一个按小时计算的计数器,可以用GetSet获取计数并重置为0。这种指令在服务端做起来是举手之劳,客户端便方便很多。
-
2.6.12版开始,Set命令已融合了Set/SetNx/SetEx三者,SetNx与SetEx可能会被废弃。
2.3 Hash
-
(String) user:101 -> {“id”:101,”name”:”calvin”…}
-
(String) user:102 -> {“id”:102,”name”:”kevin”…}
-
(Hash) user:index-> “calvin”->101, “kevin” -> 102
2.4 List
-
LSet ,按下标设置元素值。
-
LIndex,按下标返回元素。
-
LRange,不同于POP直接弹走元素,只是返回列表内一段下标的元素,是分页的最爱。
-
LTrim,限制List的大小,比如只保留最新的20条消息。
-
解决方法之一是加多一个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。
2.5 Set
-
SAdd/SRem/SIsMember/SCard/SMove/SMembers,各种标准操作。除了SMembers都是O(1)。
-
SInter/SInterStore/SUnion/SUnionStore/SDiff/SDiffStore,各种集合操作。交集运算可以用来显示在线好友(在线用户 交集 好友列表),共同关注(两个用户的关注列表的交集)。O(N),并集和差集的N是集合大小之和,交集的N是小的那个集合的大小*2。
2.6 Sorted Set
-
ZRangeByScore/ZRevRangeByScore,按分数的上下限返回元素,正数与倒数。
-
ZRemRangeByRank/ZRemRangeByScore,按排名/按分数的上下限删除元素。
-
ZCount,统计分数上下限之间的元素个数。
-
ZAdd(Add)/ZRem(Remove)/ZCard(Count),ZInsertStore(交集)/ZUnionStore(并集),Set操作,与正牌Set相比,少了IsMember和差集运算。
2.7 事务
-
它仅仅是保证事务里的操作会被连续独占的执行。因为是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的。
-
它没有隔离级别的概念,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题。
-
它不保证原子性——所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力。在redis里失败分两种,一种是明显的指令错误,比如指令名拼错,指令参数个数不对,在2.6版中全部指令都不会执行。另一种是隐含的,比如在事务里,第一句是SET foo bar, 第二句是LLEN foo,对第一句产生的String类型的key执行LLEN会失败,但这种错误只有在指令运行后才能发现,这时候第一句成功,第二句失败。还有,如果事务执行到一半redis被KILL,已经执行的指令同样也不会被回滚。
2.8 Lua Script
-
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。
-- KEYS: [1]job:sleeping, [2]job:ready -- ARGS: [1]currentTime -- Comments: result is the job id local 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]) end end
2.9 过期数据清除
3. 性能
3.1 测试结果
-
测试环境: RHEL 6.3 / HP Gen8 Server/ 2 * Intel Xeon 2.00GHz(6 core) / 64G DDR3 memory / 300G RAID-1 SATA / 1 master(writ AOF), 1 slave(write AOF & RDB)
-
数据准备: 预加载两千万条数据,占用10G内存。
-
测试工具:自带的redis-benchmark,默认只是基于一个很小的数据集进行测试,调整命令行参数如下,就可以开100条线程(默认50),SET 1千万次(key在0-1千万间随机),key长21字节,value长256字节的数据。
redis-benchmark -t SET -c 100 -n 10000000 -r 10000000 -d 256
-
测试结果(TPS): 1.SET:4.5万, 2.GET:6万 ,3.INCR:6万,4.真实混合场景: 2.5万SET & 3万GET
-
单条客户端线程时6千TPS,50与100条客户端线程差别不大,200条时会略多。
-
Get/Set操作,经过了LAN,延时也只有1毫秒左右,可以反复放心调用,不用像调用REST接口和访问数据库那样,每多一次外部访问都心痛。
-
资源监控:
1.CPU: 占了一个处理器的100%,总CPU是4%(因为总共有2CPU*6核*超线程 = 24个处理器),可见单线程下单处理器的能力是瓶颈。 AOF rewrite时另一个处理器占用50-70%。
2.网卡:15-20 MB/s receive, 3Mb/s send(no slave) or 15-20 MB/s send (with slave) 。当把value长度加到4K时,receive 99MB/s,已经到达千兆网卡的瓶颈,TPS降到2万。
3.硬盘:15MB/s(AOF append), 100MB/s(AOF rewrite/AOF load,普通硬盘的瓶颈),
3.2 为什么快
-
纯ANSI C编写。
-
不依赖第三方类库,没有像memcached那样使用libevent,因为libevent迎合通用性而造成代码庞大,所以作者用libevent中两个文件修改实现了自己的epoll event loop。微软的兼容Windows补丁也因为同样原因被拒了。
-
快,原因之一是Redis多样的数据结构,每种结构只做自己爱做的事,当然比数据库只有Table,MongogoDB只有JSON一种结构快了。
-
可惜单线程架构,虽然作者认为CPU不是瓶颈,内存与网络带宽才是。但实际测试时并非如此,见上。
3.3 性能调优
-
正视网络往返时间:
1.MSet/LPush/ZAdd等都支持一次输入多个Key。
2.PipeLining模式 可以一次输入多个指令。
3.更快的是Lua Script模式,还可以包含逻辑,直接在服务端又get又set的,见2.8 Lua Script。 -
发现执行缓慢的命令,可配置执行超过多少时间的指令算是缓慢指令(默认10毫秒,不含IO时间),可以用slowlog get 指令查看(默认只保留最后的128条)。单线程的模型下,一个请求占掉10毫秒是件大事情,注意设置和显示的单位为微秒。
-
CPU永远是瓶颈,但top看到单个CPU 100%时,就是垂直扩展的时候了。
-
持久化对性能的影响很大,见5.1持久化。
-
要熟悉各指令的复杂度,不过只要不是O(N)一个超大集合,都不用太担心。
4. 容量
4.1 最大内存
-
所有的数据都必须在内存中,原来2.0版的VM策略(将Value放到磁盘,Key仍然放在内存),2.4版后嫌麻烦又不支持了。
-
一定要设置最大内存,否则物理内存用爆了就会大量使用Swap,写RDB文件时的速度慢得你想死。
-
多留一倍内存是最安全的。重写AOF文件和RDB文件的进程(即使不做持久化,复制到Slave的时候也要写RDB)会fork出一条新进程来,采用了操作系统的Copy-On-Write策略(子进程与父进程共享Page。如果父进程的Page-每页4K有修改,父进程自己创建那个Page的副本,不会影响到子进程,父爱如山)。留意Console打出来的报告,如”RDB: 1215 MB of memory used by copy-on-write”。在系统极度繁忙时,如果父进程的所有Page在子进程写RDB过程中都被修改过了,就需要两倍内存。
-
按照Redis启动时的提醒,设置 vm.overcommit_memory = 1 ,使得fork()一条10G的进程时,因为COW策略而不一定需要有10G的free memory。
-
其他需要考虑的内存包括:
1.AOF rewrite过程中对新写入命令的缓存(rewrite结束后会merge到新的aof文件),留意”Background AOF buffer size: 80 MB”的字样。
2.负责与Slave同步的Client的缓存,默认设置master需要为每个slave预留不高于256M的缓存(见5.1持久化)。 -
当最大内存到达时,按照配置的Policy进行处理, 默认策略为volatile-lru,对设置了expire time的key进行LRU清除(不是按实际expire time)。如果沒有数据设置了expire time或者policy为noeviction,则直接报错,但此时系统仍支持get之类的读操作。 另外还有几种policy,比如volatile-ttl按最接近expire time的,allkeys-lru对所有key都做LRU。
4.2 内存占用
-
测试表明,string类型需要90字节的额外代价,就是说key 1个字节,value 1个字节时,还是需要占用92字节的长度,而上面的benchmark的记录就占用了367个字节。其他类型可根据文档自行计算或实际测试一下。
-
使用jemalloc分配内存,删除数据后,内存并不会乖乖还给操作系统而是被Redis截留下来重用到新的数据上,直到Redis重启。因此进程实际占用内存是看INFO里返回的used_memory_peak_human。
-
Redis内部用了ziplist/intset这样的压缩结构来减少hash/list/set/zset的存储,默认当集合的元素少于512个且最长那个值不超过64字节时使用,可配置。
-
用make 32bit可以编译出32位的版本,每个指针占用的内存更小,但只支持最大4GB内存。
4.4 水平分区,Sharding
-
其实,大内存加上垂直分区也够了,不一定非要沙丁一把。
-
Jedis支持在客户端做分区,局限是不能动态re-sharding, 有分区的master倒了,不能减少分区必须用slave顶上。要增加分区的话,呃…..
-
antire在博客里提到了Twemproxy,一个Twitter写的Proxy,但它在发现节点倒掉后,只会重新计算一致性哈希环,把数据存到别的master去,而不是集成Sentinel指向新由slave升级的master,像Memcached一样的做法也只适合做Cache的场景。
-
在客户端先hash出key 属于哪个分区,随便发给一台server,server会告诉它真正哪个Server负责这个分区,缓存下来,下次还有该分区的请求就直接发到地儿了。
-
Re-sharding时,会将某些分区的数据移到新的Server上,完成后各Server周知分区<->Server映射的变化,因为分区数量有限,所以通讯量不大。 在迁移过程中,客户端缓存的依然是旧的分区映射信息,原server对于已经迁移走的数据的get请求,会返回一个临时转向的应答,客户端先不会更新Cache。等迁移完成了,就会像前面那样返回一条永久转向信息,客户端更新Cache,以后就都去新server了。
5. 高可用性
5.1 持久化
-
综述: 解密Redis持久化(中文概括版), 英文原版,《Redis设计与实现》: RDB 与 AOF。
-
很多人开始会想象两者是互相结合的,即dump出一个snapshot到RDB文件,然后在此基础上记录变化日志到AOF文件。实际上两者毫无关系,完全独立运行,因为作者认为简单才不会出错。如果使用了AOF,重启时只会从AOF文件载入数据,不会再管RDB文件。
-
正确关闭服务器:redis-cli shutdown 或者 kill,都会graceful shutdown,保证写RDB文件以及将AOF文件fsync到磁盘,不会丢失数据。 如果是粗暴的Ctrl+C,或者kill -9 就可能丢失。
5.1.1 RDB文件
-
RDB是整个内存的压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件,默认是1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。
-
RDB写入时,会连内存一起Fork出一个新进程,遍历新进程内存中的数据写文件,这样就解决了些Snapshot过程中又有新的写入请求进来的问题。 Fork的细节见4.1最大内存。
-
RDB会先写到临时文件,完了再Rename成,这样外部程序对RDB文件的备份和传输过程是安全的。而且即使写新快照的过程中Server被强制关掉了,旧的RDB文件还在。
-
可配置是否进行压缩,压缩方法是字符串的LZF算法,以及将string形式的数字变回int形式存储。
-
动态所有停止RDB保存规则的方法:redis-cli config set save “”
5.1.2 AOF文件
-
操作日志,记录所有有效的写操作,等于mysql的binlog,格式就是明文的Redis协议的纯文本文件。
-
一般配置成每秒调用一次fdatasync将kernel的文件缓存刷到磁盘。当操作系统非正常关机时,文件可能会丢失不超过2秒的数据(更严谨的定义见后)。 如果设为fsync always,性能只剩几百TPS,不用考虑。如果设为no,靠操作系统自己的sync,Linux系统一般30秒一次。
-
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件,最后再rename,), 遍历新进程的内存中数据,每条记录有一条的Set语句。默认配置是当AOF文件大小是上次rewrite后大小的一倍,且文件大于64M时触发。
-
Redis协议,如set mykey hello, 将持久化成*3 $3 set $5 mykey $5 hello, 第一个数字代表这条语句有多少元,其他的数字代表后面字符串的长度。这样的设计,使得即使在写文件过程中突然关机导致文件不完整,也能自我修复,执行redis-check-aof即可。
5.1.3 读写性能
-
AOF重写和RDB写入都是在fork出新进程后,遍历新进程的内存顺序写的,既不阻塞主进程继续处理客户端请求,顺序写的速度也比随机写快。
-
测试把刚才benchmark的11G数据写成一个1.3的RDB文件,或者等大的AOF文件rewrite,需要80秒,在redis-cli info中可查看。启动时载入一个AOF或RDB文件的速度与上面写入时相同,在log中可查看。
-
Fork一个使用了大量内存的进程也要时间,大约10ms per GB的样子,但Xen在EC2上是让人郁闷的239ms (KVM和VMWare貌似没有这个毛病),各种系统的对比,Info指令里的latest_fork_usec显示上次花费的时间。
-
在bgrewriteaof过程中,所有新来的写入请求依然会被写入旧的AOF文件,同时放到buffer中,当rewrite完成后,会在主线程把这部分内容合并到临时文件中之后才rename成新的AOF文件,所以rewrite过程中会不断打印”Background AOF buffer size: 80 MB, Background AOF buffer size: 180 MB”,计算系统容量时要留意这部分的内存消耗。注意,这个合并的过程是阻塞的,如果你产生了280MB的buffer,在100MB/s的传统硬盘上,Redis就要阻塞2.8秒!!!
-
NFS或者Amazon上的EBS都不推荐,因为它们也要消耗带宽。
-
bgsave和bgaofrewrite不会被同时执行,如果bgsave正在执行,bgaofrewrite会自动延后。
-
2.4版以后,写入AOF时的fdatasync由另一条线程来执行,不会再阻塞主线程。
-
2.4版以后,lpush/zadd可以输入一次多个值了,使得AOF重写时可以将旧版本中的多个lpush/zadd指令合成一个,每64个key串一串。
5.1.4 性能调整
5.1.5 Trouble Shooting —— Enable AOF可能导致整个Redis被Block住,在2.6.12版之前
-
官方文档,由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秒的数据丢失。
5.2 Master-Slave复制
5.2.1 概述
-
slave可以在配置文件、启动命令行参数、以及redis-cli执行SlaveOf指令来设置自己是奴隶。
-
测试表明同步延时非常小,指令一旦执行完毕就会立刻写AOF文件和向Slave转发,除非Slave自己被阻塞住了。
-
比较蠢的是,即使在配置文件里设了slavof,slave启动时依然会先从数据文件载入一堆没用的数据,再去执行slaveof。
-
“Slaveof no one”,立马变身master。
-
2.8版本将支持PSYNC部分同步,master会拨出一小段内存来存放要发给slave的指令,如果slave短暂的断开了,重连时会从内存中读取需要补读的指令,这样就不需要断开两秒也搞一次全同步了。但如果断开时间较长,已经超过了内存中保存的数据,就还是要全同步。
-
Slave也可以接收Read-Only的请求。
5.2.2 slaveof执行过程,完全重用已有功能,非常经济
-
先执行一次全同步 — 请求master BgSave出自己的一个RDB Snapshot文件发给slave,slave接收完毕后,清除掉自己的旧数据,然后将RDB载入内存。
-
再进行增量同步 — master作为一个普通的client连入slave,将所有写操作转发给slave,没有特殊的同步协议。
5.2.3 Trouble Shooting again
5.3 Fail-Over
5.3.1 主要执行过程
-
Sentinel每秒钟对所有master,slave和其他sentinel执行Ping,redis-server节点要应答+PONG或-LOADING或-MASTERDOWN.
-
如果某一台Sentinel没有在30秒内(可配置得短一些哦)收到上述正确应答,它就会认为master处于sdown状态(主观Down)
-
它向其他sentinel询问是否也认为该master倒了(SENTINEL is-master-down-by-addr ), 如果quonum台(默认是2)sentinel在5秒钟内都这样认为,就会认为master真是odown了(客观Down)。
-
此时会选出一台sentinel作为Leader执行fail-over, Leader会从slave中选出一个提升为master(执行slaveof no one),然后让其他slave指向它(执行slaveof new master)。
5.3.2 master/slave 及 其他sentinel的发现
5.3.3 自定义reconfig脚本
-
sentinel在failover时还会执行配置文件里指定的用户自定义reconfig脚本,做用户自己想做的事情,比如让master变为slave并指向新的master。
-
脚本的将会在命令行按顺序传入如下参数: <master-name> <role(leader/observer)> <state(上述三种情况)> <from-ip> <from-port> <to-ip> <to-port>
-
脚本返回0是正常,如果返回1会被重新执行,如果返回2或以上不会。 如果超过60秒没返回会被强制终止。
-
一是如果master 主动shutdown,比如系统升级,有办法主动通知sentinel提升新的master,减少服务中断时间。
-
二是比起redis-server太原始了,要自己丑陋的以nohup sentinel > logfile 2>&1 & 启动,也不支持shutdown命令,要自己kill pid。
5.4 Client的高可用性
6. 运维
6.1 安装
-
安装包制作:没有现成,需要自己编译,自己写rpm包的脚本,可参考utils中的install_server.sh与redis_init_script。
但RHEL下设定script runlevel的方式不一样,redis_init_script中要增加一句 “# chkconfig: 345 90 10″ ,而install_server.sh可以删掉后面的那句“chkconfig –level 345 reis” -
云服务:http://redis-cloud.com/ 在Amazon、Heroku、Windows Azure、App Frog上提供云服务,供同样部署在这些云上的应用使用。
-
CopperEgg统计自己的用户在AWS上的数据库部署:mysqld占了50%半壁江山, redis占了18%排第二, mongodb也有11%, cassandra是3%,Oracle只有可怜的2%。
-
Chef Recipes:brianbianco/redisio,活跃,同步更新版本。
6.2 部署模型
-
Redis只能使用单线程,为了提高CPU利用率,有提议在同一台服务器上启动多个Redis实例,但这会带来严重的IO争用,除非Redis不需要持久化,或者有某种方式保证多个实例不会在同一个时间重写AOF。
-
一组sentinel能同时监控多个Master。
-
有提议说环形的slave结构,即master只连一个slave,然后slave再连slave,此部署有两个前提,一是有大量的只读需求需要在slave完成,二是对slave传递时的数据不一致性不敏感。
6.3 配置
6.3.1 三条路
-
可以配置文件中编写。
-
可以在启动时的命令行配置,redis-server –port 7777 –slaveof 127.0.0.1 8888。
-
云时代大规模部署,把配置文件满街传显然不是好的做法, 可以用redis-cli执行Config Set指令, 修改所有的参数,达到维护人员最爱的不重启服务而修改参数的效果,而且在新版本里还可以执行 Config Rewrite 将改动写回到文件中,不过全部默认值都会打印出来,可能会破坏掉原来的文件的排版,注释。
6.3.2 安全保护
-
在配置文件里设置密码:requirepass foobar。
-
禁止某些危险命令,比如残暴的FlushDB,将它rename成”":rename-command FLUSHDB “”。
6.4 监控与维护
6.4.1 监控指令
6.4.2 Trouble Shooting支持
-
日志可以动态的设置成verbose/debug模式,但不见得有更多有用的log可看,verbose还会很烦的每5秒打印当前的key情况和client情况。指令为config set loglevel verbose。
-
最爱Redis的地方是代码只有2.3万行,而且编码优美,而且huangz同学还在原来的注释上再加上了中文注释——Redis 2.6源码中文注释版 ,所以虽然是C写的代码,虽然有十年没看过C代码,但这几天trouble shooting毫无难度,一看就懂。
-
Trobule shotting的经历证明antirez处理issue的速度非常快(如果你的issue言之有物的话),比Weblogic之类的商业支持还好。
6.4.3 持久化文件维护
-
如果AOF文件在写入过程中crash,可以用redis-check-aof修复,见5.1.2
-
如果AOF rewrite和 RDB snapshot的过程中crash,会留下无用的临时文件,需要定期扫描删除。
6.4.4 三方工具
-
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自己写的,统计数据分布情况。
7. Java Driver
7.1 Driver选择
7.2 Jedis的细节
-
不要用不限时的blocking popup,传多一个超时时间参数,如5秒。
-
找地方将调用blocking popup的jedis保存起来,shutdown时主动调用它的close。
7.3 Redis对Client端连接的处理
-
Redis默认最大连接数是一万。
-
Redis默认不对Client做Timeout处理,可以用timeout 项配置,但即使配了也不会非常精确。
8. Windows的版本
9. 成功案例
-
Dashboard的海量通知的存储。
-
Dashboard的二级索引。
-
存储海量短链接的HBase前面的缓存。
-
Gearman Job Queue的存储。
-
正在替换另外30台memcached。
-
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,好穷好轻量。
-
Discourge,号称是为下一个十年打造的论坛系统, We use Redis for our job queue, rate limiting, as a cache and for transient data,刚好和我司的用法一样。
10. In SpringSide
10.1 Jedis Template
10.2 Scheduler与Master Elector
10.3 Showcase中的Demo
附录
附录1: 对redis.conf默认配置的修改
Master上
-
daemonize no -> yes ,启动daemonize模式,注意如果用daemon工具启动redis-server时设回false。
-
logfile stdout -> /var/log/redis/redis.log ,指定日志文件
-
注释掉RDB的所有触发规则,在Master不保存RDB文件。
-
dir ./ -> /var/data/redis,指定持久化文件及临时文件目录.
-
maxmemory,设置为可用内存/2.
-
(可选)appendonly no->yes,打开AOF文件.
-
auto-aof-rewrite-percentage 100, 综合考虑硬盘大小,可接受重启加载延时等尽量的大,减少AOF rewrite频率.
-
auto-aof-rewrite-min-size 64mb,同上,起码设为5G.
-
client-output-buffer-limit slave 256mb 64mb 60. 考虑Traffic及Slave同步是RDB加载所需时间,正确设置避免buffer撑爆client被关掉后又要重新进行全同步。
-
安全配置,可选。
Slave上
-
设置RDB保存频率,因为RDB只作为Backup工具,只保留15分钟的规则,设置为15分钟保存一次就够了save 900 1。
-
(可选)slaveof 设置master地址,也可动态设定。
-
repl-timeout 60, 适当加大比如120,避免master实际还没倒掉就认为master倒了。
附录2:版本变更历史
-
3.0.1版 2013-7-10,在微博发布后反应良好,持续修改。
-
3.0版 2013-6-29,在公司Workshop后修订,提高wiki的可读性而不只是简单的记录知识点。### 附录3:其他参考资料
-
Redis的几个认识误区by Tim yang。