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. uva 10718 Bit Mask (位运算)

    uva 10718  Bit Mask  (位运算) Problem A Bit Mask Time Limit 1 Second In bit-wise expression, mask is a ...

  2. 05_Elasticsearch 单模式下API的增删改查操作

    05_Elasticsearch 单模式下API的增删改查操作 安装marvel 插件: zjtest7-redis:/usr/local/elasticsearch-2.3.4# bin/plugi ...

  3. javabeans的运用

    javabeans的运用 对javabean的使用我开始严重的郁闷,跟着书上说的做,但是总是不成功.后来别人说我是基础不牢靠.我觉得应该从servlet学起然后再加进入JSP学是非常快的,对于JAVA ...

  4. 《Java程序员面试笔试宝典》之Java程序初始化的顺序是怎样的

    在Java语言中,当实例化对象时,对象所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建对象. Java程序的初始化一般遵循以下三个原则(以下三原则优 ...

  5. .NET 中使用 HttpWebResponse 时 Cookie 的读取

    今天把一个网站登录配置到以前写的蜘蛛程序中,发现不能成功登录.检查后才发现,那个网站在登录成功后,输出了一个特殊路径的 Cookie,由于是使用 HttpWebRequest.Cookies 来获取的 ...

  6. linux cache swap 以及 虚拟内存等

    提出四个问题及解答: 1)若进程在运行过程中,物理内存不足会发生什么? 2)为何进程在占用物理内存不变的情况下,系统的物理内存会增加? 3)为何程序的大小大于实际占用的物理内存?(假如程序30M,却只 ...

  7. html_day3

    总结学习html的第一天 表格的结构说明 <table></table> <tr></tr> <td></td> <th& ...

  8. linux基础内容学习一:linux下的分区及安装

    linux看系统版本信息 uname -a 如果显示为i386,i686则为32位系统,如果为x86_64则为64位 一块硬盘最多可以有四个主分区其中一个主分区可以用一个扩展分区替换,在这个扩展分区中 ...

  9. nginx启动报错(1113: No mapping for the Unicode character exists in the target multi-byte code page)

    使用windows版本的nginx启动时遇到(1113: No mapping for the Unicode character exists in the target multi-byte co ...

  10. CppCon - Modern Template Metaprogramming 杂记

    2014年底才看到github和channel9上有CppCon2014的视频和资料,顿时激动不已.最近小生也一直在研习CppCon2014中令人兴奋的内容.这篇鄙文就是小生学习了<Modern ...