http://blog.csdn.net/mengxianhua/article/details/8961713

2013-05-22 18:00 13315人阅读 评论(0) 收藏 举报
 分类:
Redis(12) 
开源项目SpringSide发起人肖桦(@江南白衣Calvin)在动手试用了Redis数据库一周之后,总结了一些关于Redis的常识,在这里分享给大家。

  1. Overview

  1.1 资料

《The Little Redis Book》最好的入门小册子,可以先于一切文档之前看,免费版,会不时更新。
Redis 命令中文版
作者的微博
NoSQL Fan里的Redis分类
《Redis Cookbook》(O'Reilly Media, 2011)
《Redis in Action》MEAP版,只有放出来的第一章。

  1.2 优缺点

非常非常的快, 有测评说比Memcached还快。
丰富的数据结构,超越了一般的Key-Value数据库,组合使用各种结构,限制Redis用途的只会是你自己的想象力, Redis在互联网上的11种常见用例。
因为是个人作品,Redis的代码量也就1万行左右,大多选择了比较Simple的做法,使得普通公司而不是文艺公司也可以吃透它。
作者认为Redis 2.6版已经成熟,2013年的精力会放在HA和Scalable上,也就是Redis-Sentinel和Redis-Cluster了。
  1.3 八卦

作者是意大利的Salvatore Sanfilippo(antirez),又是VMWare大善人聘请了他专心写Redis。
antirez和我一样不喜欢搞什么咨询服务,所以Commercial Support是不会有了。
默认端口6379,是手机按键上MERZ对应的号码,意大利歌女Alessia Merz是antirez和朋友们认为愚蠢的代名词。
  2. 数据结构

  2.1 Key

Key 可以是任意类型,最后都存成byte[]
Key 不能太长,比如1024字节,但作者也不追求太短,要表达清楚意思才好,作者建议用":"分隔表名,用"."作为单词间的连接。
KEYS显示所有的key,支持通配符 "KEYS a*" , "keys a?c",但不建议在生产环境大数据量下使用。
SORT,对集合按数字或字母顺序排序后返回,或者存到另一个List,还可以关联到外部Key等。因为会耗用CPU,有时会安排到slave上执行。
EXPIRE/EXPREAT/PERSIST/TTL/,关于Key超时的操作,默认以秒为单位,也有p字头的以毫秒为单位的版本。
其他命令: EXISTS,DEL,RENAME/RENAMENX(仅当new key不存在时),MOVE/MIGRATE(实例内从此db到彼db/从此实例到彼实例),RANDOMKEY,TYPE/Object(Key的类型/对象编码类型,空置时间),DUMP/RESTORE(value值的持久化)
  2.2 String

  最普通的key-value,除了支持最基本的get/set, Redis也很喜欢添加一些简便的指令,在服务端做起来是举手之劳,客户端便方便很多。

incr/decr/incrby/incrbyfloat, 如果key还不存在时创建key并设原值为0。
setEx/pSetEx, Set + Expire 的简便写法,p字头以毫秒为单位。
setNx, key不存在时才put进去。
getset, 设置新值,返回旧值。
mget/mset/msetex, 一次get/set多个key。
getbit/setbit/bitop/bitcount bitmap玩法,比如统计今天的访问用户,每个用户有一个offset,今天进来的话就把那个位为1。
append/setrange/getrange,只对特定的数据格式比如字段定长的有用,json格式就没用。
  2.3 Hash

  Key-HashMap结构,相比2.2中的JSON格式Value,可以只读取/更新对象的某些属性,有些属性超长就让它一边呆着不动。。

  另一个用法是用来建索引。比如User对象,除了id有时还要按name来查询,可以建一个Key为user:index:name的Hash,在插入User对象时(set user:101 {"id":101,"name":"calvin"}), 顺便往这个hash插入一条(hset user:index:name calvin 101),这时calvin作为hash里的一个key,值为101。按name查询的时候,用hget user:index:name calvin 就能从名为calvin的key里取出id。

  2.4 List

  Redis里可以当双向链表来用,还提供blocking版本的pop函数,可以当Message Queue来用。

  不过List并没有JMS的ack机制,如果消费者把job给Pop走了又没处理完就死机了怎么办? 解决方法之一是加多一个sorted set,以分发时间为score,用户把job做完了之后要去消掉它。

  除了List标准的双向POP/PUSH外,还支持对队列内容的直接操作,比如LREM/LSET/LINSERT/LINDEX。

  另外经常用LTRIM限制List的大小,比如只保留最新的20条消息。LRANGE不同于POP直接弹走元素,只是返回列表内一段下标的元素。LLEN获取列表的长度。

  2.5 Set

  Set就是Set,还提供一些交集,并集,差集的集合操作。

  2.6 Sorted Set

  有序集,元素放入集合的时候要同时提供该元素的分数。

ZRANGE/ZREVRANGE 按排名的上下限返回元素,正数与倒数。
ZRANGEBYSCORE/ZREVRANGEBYSCORE 按分数的上下限返回元素,正数与倒数。
ZREMRANGEBYRANK/ZREMRANGEBYSCORE 按排名/按分数删除元素。
ZCOUNT 统计分数上下限之间的元素个数。
ZRANK/ZREVRANK 显示某个元素的正倒序的排名。
ZSCORE/ZINCRBY 显示元素的分数/增加元素的分数。
ZADD/ZREM/ZCARD/ZINTERSTORE/ZUNIONSTORE 集合操作与SET相同,少了个差集的操作。
  2.7 事务

  用Multi/Exec/Discard实现, 隔离级别是这边事务一天不提交,那边另一个事务还是看到旧的值。 还有个Watch指令,起到CAS的效果,如果事务提交时,Key的值已被别的事务改变,事务会被打断。

  2.8 Lua Script

  Redis2.6内置的Lua Script支持,可以在Redis的Server端一次过运行大量逻辑。

整个Script默认是在一个事务里的。
Script里涉及的所有Key尽量用变量,从外面传入,使Redis一开始就知道你要改变哪些key。
EVAL每次传输一整段Script比较费带宽,可以先用SCRIPT LOAD载入script,返回哈希值。然后用EVALHASH执行。
内置的LUA库里还很贴心的带了CJSON,可以处理JSON字符串。
  3. 性能

速度太快了,用光了带宽也测不出极限。如果是本地socket直连,incr可以去到很吓人的几十万TPS。
普通的get/set操作,经过了LAN,延时也只有1毫秒左右,可以放心使用,不用像调用REST接口和访问数据库那样,多一次外部访问都心痛。
自带的redis-benchmark默认只是基于一个很小的数据集进行测试,但可调整命令行参数如 redis-benchmark -r 10000000 -n 10000000 -d 128 -t SET,GET 就可以默认开50条线程,SET 6M条左右(random)key是21字节长,value是128字节长的数据进去, 再GET出来。
如果要一次发送多条指令,PipeLining模式能让性能更快,因为它在设计上正视了网络往返的时间。
更快的是Lua Script模式,还可以包含逻辑,直接在服务端又get又set的 (见2.8 Lua Script)。
单线程单CPU架构,但作者认为CPU不是瓶颈,内存与网络带宽才是。
发现执行缓慢的命令,可配置执行超过多少时间的指令算是缓慢指令(默认10毫秒,不含IO时间),可以用slowlog get 指令查看(默认只保留最后的128条)。单线程的模型下,某个请求占掉10毫秒是件大事情。
  4. 容量

  最大内存:

一定要设置最大内存,否则物理内存用爆了就会大量使用Swap,写RDB文件时的速度慢得你想死。
多留一倍内存是最安全的。重写AOF文件和RDB文件的进程(即使不做持久化,复制到Slave的时候也要写RDB)会fork出一条新进程来,采用了操作系统的Copy-On-Write策略(如果父进程的内存没被修改,子进程与父进程共享Page。如果父进程的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.
当最大内存到达时,按照配置的Policy进行处理, 默认policy为volatile-lru, 对设置了expire time的key进行LRU清除(不是按实际expire time)。如果沒有数据设置了expire time或者policy为noeviction,则直接报错,但此时系统仍支持get之类的读操作。 另外还有几种policy,比如volatile-ttl按最接近expire time的,allkeys-lru对所有key都做LRU。
原来2.0版的VM(将Value放到磁盘,Key仍然放在内存),2.4版后又不支持了。
  内存占用:

测试表明,stirng类型需要90字节的额外代价,就是说key1个字节,value一个字节时,还是需要占用92字节的长度,而上述的benchmark的记录就占用了239个字节。
用make 32bit可以编译出32位的版本,每个指针占用的内存更小,但只支持最大4GB内存。
  Sharding:

Jedis支持在客户端做分区,局限是不能动态re-sharding, 有分区的master倒了,必须用slave顶上。要增加分区的话,呃.....
Redis-Cluster是今年工作重点,支持automatic re-sharding, 采用和Hazelcast类似的算法,总共有N个分区,每台Server负责若干个分区。客户端先hash出key 属于哪个分区,然后发给负责这个分区的Server。Re-sharding时,会将某些分区的数据移到新的Server上,然后各Server周知分区<->Server映射的变化,因为分区数量有限,所以通讯量不大。 在迁移过程中,原server对于已经迁移走的数据的get请求,会回一个临时转向的应答。
  5. 高可用性

  5.1 持久化

  综述: 解密Redis持久化(中文概括版),英文原版

  RDB文件:

整个内存的压缩过的Snapshot,RDB的数据结构, 可以配置写Snapshot的复合触发条件,默认是60秒内改了1万次或300秒内改了10次或900秒内改了1次。
RDB在写入过程中,会连内存一起Fork出一个新进程,遍历新进程内存中的数据写RDB。
先写到临时文件再Rename,这样外部程序对RDB文件的备份和传输过程是安全的。而且即使写新快照的过程中Server被强制关掉了,旧的RDB文件还在。
可配置是否进行压缩,方法是是字符串的LZF算法 和String形式的数字变回int形式存储。
  AOF文件:

append only的操作日志,等于mysql的binlog,记录所有有效的写操作,格式是明文的Redis协议的纯文本文件。
一般配置成每秒调用一次fsync将数据写到磁盘,坏处是操作系统非正常关机时,可能会丢1秒的数据。 如果设为fsync always,性能只剩几百TPS,不用考虑。
如果使用了AOF,重启时只会从AOF文件载入数据,不会管RDB文件。
AOF文件过大时,会fork出一条新进程来将文件重写(也是先写临时文件再rename), 遍历新进程的内存中数据,每条记录有一条的Set语句。默认配置是当AOF文件大小是上次rewrite后的大小的一倍时触发。
Redis协议的内容,如set mykey hello, 将持久化成*3 $3 set $5 mykey $5 hello, 第一个数字代表这条语句有多少元,其他的数字代表后面字符串的长度。这样的设计,使得即使在写文件过程中突然关机导致文件不完整,也能自我修复,执行redis-check-aof即可。
    RDB不会实时写入数据,而且如果同时使用两者,但服务器重启只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库,快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

  读写性能:

AOF重写和RDB写入都是在fork出进程后,遍历新进程内存顺序写的,既不影响主进程,顺序写的速度也比随机写快,在普通PC服务器上把刚才的1.5G数据写成一个200M的RDB文件大约8秒, 启动时载入一个1.4G的AOF文件大约13秒。
2.4版以后,lpush可以一次push多个值了,使得AOF重写时可以将旧版本中的多个lpush指令合成一个。
有人建议设置no-appendfsync-on-rewrite 为 yes,aof rewrite时就不执行fsync了,先都存在内存里,减少IO资源争用。 当然这样会丢数据。
Fork一个使用了大量内存的进程也要时间,大约10ms per GB的样子,各种系统的对比。
  其他:

正确关闭服务器:redis-cli shutdown 或者 kill,都会graceful shutdown,保证写RDB文件以及将AOF文件fsync到磁盘,不会丢失数据。 如果是Ctrl+C,或者kill -9 就会丢失数据。
执行指令bgsave 可触发rdb存盘,bgrewriteaof可触发aof重写。
  5.2 Master-Slave复制

可以在配置文件、命令行参数、以及执行SLAVEOF指令的来设置。
当前版本,一旦执行SlaveOF, slave会清掉自己的所有数据,执行一次全同步:Master要bgsave出自己的一个RDB文件,发给Slave。然后再增量同步: Master作为一个普通的client连入slave,将所有写操作转发给slave,没有特殊的同步协议。
作者在2.8版本中将支持PSYNC部分同步
测试表明同步延时非常小。
有建议只在Slave上写RDB和AOF文件,但这样master启动时就需要从slave copy文件,fail-over脚本也更复杂。只要有足够内存,master平时IO也不高的话,还是简化架构更好。
  5.3 Fail-Over

  5.3.1 概述

  Redis-sentinel是2.6版开始加入的另一组独立运行的节点, 提供自动Fail Over的支持。

每秒钟对所有master,slave和其他sentinel执行ping,redis-server节点要应答+PONG或-LOADING或-MASTERDOWN.
如果某一台Sentinel没有在30秒内(可配置得短一些哦)收到上述正确应答,它就会认为master处于sdown状态(主观Down)
它向其他sentinel询问是否也认为master倒了(SENTINEL is-master-down-by-addr ), 如果quonum台(默认是2)sentinels在5秒钟内都这样认为,就会认为master真是odown了(客观Down)。
此时会选出一台sentinel作为Leader执行fail-over, Leader会从slave中选出一个提升为master(执行slaveof none),这台slave必须状态正常,而且INFO显示它与master的复制连接并没有断开太久。然后让其他slave指向它(执行slaveof new master)。
    5.3.2 master/slave 及 其他sentinels的发现

  master地址在sentinel的配置文件里, sentinel会每10秒一次向master发送INFO,知道master的slave有哪些。 如果master已经变为slave,sentiel会分析INFO的应答指向新的master。所以当sentiel重启时,它的配置文件里master的地址并没变,那它仍然会去找old master,然后被redirect到新的master。但如果old master还没起来,或者old master没把自己变成slave,悲剧就可能发生。

  另外,sentinel会在master上建一个pub/sub channel,通告各种信息,比如+sdown/-sdown, 而且sentinel们也是通过接收pub/sub channel上的+sentinel的信息发现彼此,因为每台sentinel每5秒会发送一次__sentinel__:hello,宣告自己的存在。

  5.3.3 自定义脚本

sentinel在failover时还会执行配置文件里指定的用户reconfig脚本,让master变为slave并指向新的master。
脚本在如下时机被调用: 1. slave开始提升成master,2.所有slave都已指向新master,3.各种原因提升被终止。
脚本的将会在命令行按顺序传入如下参数: <master-name> <role(leader/observer)> <state(上述三种情况)> <from-ip> <from-port> <to-ip> <to-port>
脚本返回0是正常,如果返回1会被重新执行,如果返回2或以上不会。 如果超过60秒没返回会被强制终止。
  另一种notify脚本在收到任何pub/sub信息时都会调用,让你去通知O&M系统。

  5.3.4 Client集成

  client中执行语句SENTINEL get-master-addr-by-name mymaster 可获得当前master的地址。 但是Jedis还没集成sentinel,只有一个热心网友提交了pull request

  淘宝的Tedis driver,使用了完全不同的思路,不基于Sentinel,而是多写随机读,学术名词是ReadOne-WriteAll-tx(see NoSQL数据库的分布式算法), 一开始就同步写入到所有节点,读的话随便读一个还活着的节点就行了。(但节点死掉重新起来后怎么重新同步?什么时候可以重新作为一个可选的master?)

  Redis作者也在博客里抱怨怎么没有人做Dynamo-style 的client。

  5.4 Geographic-Redundancy

  一个方法是用rsync等工具同步RDB文件,但RDB文件是非常不实时的。

  如果要求更高,可以自己写程序读取AOF文件或MONITOR,把指令转发给远处的站点,反正里头的内容就是Redis协议。

  6. 维护

  配置

  可以在配置文件中编写, 也可以在启动时的命令行配置, 如redis-server --port 7777 --slaveof 127.0.0.1 8888,还可以在cli中执行CONFIG GET, 可以达到不重启服务的修改配置参数的效果。

  监控

  Redis监控技巧 有详细的介绍。

  INFO指令将返回非常丰富的信息。

着重监控检查内存使用,比如是否已接近上限,比如有无大量使用swap(used_memory - used_memory_rss)
还有AOF与RDB文件的保存情况,master-slave的关系也要重点监控。
STAT中信息还包括key命中率,所有命令的执行次数,所有client连接数量等, CONFIG RESETSTAT 可重置。
  其他

SlowLog 检查慢操作(见2.性能)
配置sentinel的notify.sh脚本对所有事件告警或自己用PING/INFO监控节点状态(见5.3.3)
MONITOR可以显示Server收到的所有指令,可以用于debug。
Redis live, 基于Python的DashBoard,使用INFO和MONITOR获得系统情况和指令统计分析。
Instagram的Redis Faina,基于Python,使用MONITOR对指令进行统计分析.
Redis-rdb-tools 基于Python,可以分析RDB文件,比如每条Key对应value所占的大小,还可以将RDB dump成文本文件然后进行比较,还可以将RDB输出成JSON格式。
Redis作者自己写的Redis Sampler,基于Ruby,统计数据分布情况。
  维护

用redis-check-aof/redis-check-dump 修复烂掉的文件。
在系统闲时调用 bgrewriteaof 重写AOF文件。
  7. 其他

  过期数据清除 ,过期数据的清除从来不容易,Redis使用了一种相对务实的做法

当client主动get的时候会先进行判断。
如果clien永远都不再get那条key呢? 它会在后台,每秒10次的执行如下操作: 随机选取100个key校验是否过期,如果有25个以上的key过期了,立刻随机选取下100个key,可见如果数据不被主动get,它什么时候最终被清理掉是未知的。
上述主动清理只会在master进行,slave们会收到从master发过来的DEL指令,master的AOF文件也会插入一条DEL。
  Redis如何处理客户端连接

  8. Java Driver

  各个Driver好像只有Jedis比较活跃。

  不需要Spring Data Redis的封装,因为Jedis已足够简单,所以它没有像对MongoDB java driver的封装那样能简化代码,所谓屏蔽各种底层driver的差异并不太吸引人,因为我就没打算选其他几种driver。

  Jedis基于Apache Commons Pool做的连接池,默认最大连接数只有8,一般需要重新设置。

  9. Windows的版本

  Windows版本方便单机调测,但Redis并没有提供,好在微软提供了一个,暂时基于Redis 2.4版本。 https://github.com/MSOpenTech/redis 因为github现在已经没有Download服务了,所以编译好的可执行文件藏在这里 https://github.com/MSOpenTech/redis/tree/2.4/msvs/bin/release

原文出处:https://github.com/springside/springside4/wiki/redis 
TechTarget中国原创内容,原文链接:http://www.searchdatabase.com.cn/showcontent_70423.htm

 
 

关于Redis的一些常识的更多相关文章

  1. Redis命令大全(超详细)

    一:序 其实本文的命令大家都可以去官网学习,但是我出这篇文章只是以更直观的方式来解读官网上的命令,让大家一眼可以看得懂,看的明白: 注意:我全文使用的Redis版本为 6.2.x 版本,低版本可能有些 ...

  2. C# Memory Cache 踩坑记录

    背景 前些天公司服务器数据库访问量偏高,运维人员收到告警推送,安排我团队小伙伴排查原因. 我们发现原来系统定期会跑一个回归测试,该测运行的任务较多,每处理一条任务都会到数据库中取相关数据,高速地回归测 ...

  3. 你真的了解字典(Dictionary)吗? C# Memory Cache 踩坑记录 .net 泛型 结构化CSS设计思维 WinForm POST上传与后台接收 高效实用的.NET开源项目 .net 笔试面试总结(3) .net 笔试面试总结(2) 依赖注入 C# RSA 加密 C#与Java AES 加密解密

    你真的了解字典(Dictionary)吗?   从一道亲身经历的面试题说起 半年前,我参加我现在所在公司的面试,面试官给了一道题,说有一个Y形的链表,知道起始节点,找出交叉节点.为了便于描述,我把上面 ...

  4. Redis cluster学习 & Redis常识 & sort操作

    Redis中的5种数据类型String.Hash.List.Set.Sorted Set. Redis源码总代码一万多行. 这篇文章有一些Redis "常识" http://www ...

  5. 关于Redis的常识(推荐)

    原文出处: https://github.com/springside/springside4/wiki/redis 版本:V3.0.3 2013-8-1 (@江南白衣版权所有,转载请保留出处) 1. ...

  6. 关于Redis的常识

    原文出自:http://blog.jobbole.com/44476/ 版本:V3.0.2 2013-7-13 (江南白衣版权所有,转载请保留出处) 1. Overview 1.1 资料 <Th ...

  7. Redis的常识

    https://github.com/springside/springside4/wiki/redis 1. Overview 1.1 资料 <The Little Redis Book> ...

  8. Redis 【常识与进阶】

    Redis 简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久 ...

  9. 妈妈再也不用担心别人问我是否真正用过redis了

    1. Memcache与Redis的区别 1.1. 存储方式不同 1.2. 数据支持类型 1.3. 使用底层模型不同 2. Redis支持的数据类型 3. Redis的回收策略 4. Redis小命令 ...

随机推荐

  1. IQC IPQC FQC OQC QA QE SQE CQS QM 简介区别

    IQC Incoming quality control 来料质量控制.是企业产品过程质量的第一关,在这个部门工作的同事最主要的是要注意文件和资料,即:图纸 样品承认书 样板 ***配件检验规范 ,还 ...

  2. NOR flash和NAND flash区别,RAM 和ROM区别d

    ROM和RAM指的都是半导体存储器,ROM是Read Only Memory的缩写,RAM是Random Access Memory的缩写.ROM在系统停止供电的时候仍然可以保持数据,而RAM通常都是 ...

  3. LCD的背光及觸摸屏

    液晶的发现可追溯到19 世纪末,1888 年被奥地利植物学家发现.它是一种在一定温度范围内呈现既不同于固态.液态,又不同于气态的特殊物质态.既具有晶体所具有的各向异性造成的双折射性,又具有液体所特有的 ...

  4. el简略说明与11个隐含对象

      El的特点:   el语法:     El11个隐含对象:  

  5. 【转】Android下编译jni库的二种方法(含示例)

    原文网址:http://blog.sina.com.cn/s/blog_3e3fcadd01011384.html 总结如下:两种方法是:1)使用Android源码中的Make系统2)使用NDK(从N ...

  6. < welcome > 一起学习,进步,分享。

    现在时间:2014-3-24 hello world my blog. 第一次做博客,欢迎各路朋友指教.慢慢的分享学习到得东西,本人目前正在做IOS,也在学习数据库简单地应用. 作为一个IOS开发者, ...

  7. spring bean初始化和销毁

    spring bean的创建与消亡由spring容器进行管理,除了使用<bean><property/></bean>进行简单的属性配置之外,spring支持更人性 ...

  8. 方案:解决 wordpress 中 gravatar 头像被墙问题

    Gravatar头像具有很好的通用性,但是却遭到了无辜的拦截,对于无法加载头像URL,我们在WordPress系统中通过修改默认的URL链接可以达到恢复头像的功能. 修改文件路径为 /wp-inclu ...

  9. (转)Tomcat 7 访问 Manager 和 Host Manager

    配置好 Tomcat 7.0 后,在 tomcat-users.xml 中配置用户角色来访问 localhost:8080 的这样三个按钮总出现问题: Server Status Manager Ap ...

  10. SqlDependency不起作用

    今天使用SqlDependency,结果不起作用,失效,不管数据库怎么修改,这边都没反应,OnChange事件总是不执行,很奇怪.我打开msdn里的例子,代码复制出来,结果没问题,能执行,那剩下来的问 ...