版本:V3.1 2014-3-36 (@江南白衣版权所有,转载请保留出处),针对Redis 2.8版。

1. Overview

1.1 资料

1.2 优缺点

非常非常的快,有测评说比Memcached还快(当大家都是单CPU的时候),而且是无短板的快,读写都一般的快,所有API都差不多快,也没有MySQL Cluster、MongoDB那样更新同一条记录如Counter时慢下去的毛病。

丰富的数据结构,超越了一般的Key-Value数据库而被认为是一个数据结构服务器。组合各种结构,限制Redis用途的是你自己的想象力,作者自己捉刀写的用途入门。

因为是个人作品,Redis2.6版只有2.3万行代码,Keep it simple的死硬做法,使得普通公司而不需淘宝那个级别的文艺公司也可以吃透它。 Redis宣言就是作者的自白,我最喜欢其中的“代码像首诗”,”设计是一场与复杂性的战斗“,“Coding是一件艰苦的事情,唯一的办法是享受它。如果它已不能带来快乐就停止它。为了防止这一天的出现,我们要尽量避免把Redis往乏味的路上带。”

让人又爱又恨的单线程架构,使得代码不用处理平时最让人头痛的并发而大幅简化,也不用老是担心作者的并发有没有写对,但也带来单CPU的瓶颈,而且单线程被慢操作所阻塞时,其他请求的延时变得不确定。

那Redis不是什么?

1.3 Feature速览

1.4 八卦

2. 数据结构

2.1 Key

2.2 String

最普通的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指令:

GetBit/SetBit/BitOp,与或非/BitCount, BitMap的玩法,比如统计今天的独立访问用户数时,每个注册用户都有一个offset,他今天进来的话就把他那个位设为1,用BitCount就可以得出今天的总人数。

Append/SetRange/GetRange/StrLen,对文本进行扩展、替换、截取和求长度,只对特定数据格式如字段定长的有用,json就没什么用。

2.3 Hash

Key-HashMap结构,相比String类型将这整个对象持久化成JSON格式,Hash将对象的各个属性存入Map里,可以只读取/更新对象的某些属性。这样有些属性超长就让它一边呆着不动,另外不同的模块可以只更新自己关心的属性而不会互相并发覆盖冲突。

另一个用法是土法建索引。比如User对象,除了id有时还要按name来查询。可以有如下的数据记录:

底层实现是hash table,一般操作复杂度是O(1),要同时操作多个field时就是O(N),N是field的数量。

2.4 List

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开始,队列从左到右算,下标为负数时则从右到左。

复杂度也是O(N),其中LSet的N是List长度,LIndex的N是下标的值,LRange的N是start的值+列出元素的个数,因为是链表而不是数组,所以按下标访问其实要遍历链表,除非下标正好是队头和队尾。LTrim的N是移除元素的个数。

在消息队列中,并没有JMS的ack机制,如果消费者把job给Pop走了又没处理完就死机了怎么办?

2.5 Set

Set就是Set,可以将重复的元素随便放入而Set会自动去重,底层实现也是hash table。

2.6 Sorted Set

有序集,元素放入集合时还要提供该元素的分数,默认从小到大排列。

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很大那谁也没办法了。

2.7 事务

用Multi(Start Transaction)、Exec(Commit)、Discard(Rollback)实现。 在事务提交前,不会执行任何指令,只会把它们存到一个队列里,不影响其他客户端的操作。在事务提交时,批量执行所有指令。《Redis设计与实现》中的详述。

注意,Redis里的事务,与我们平时的事务概念很不一样:

Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行。

2.8 Lua Script

Redis2.6内置的Lua Script支持,可以在Redis的Server端一次过运行大量逻辑,就像存储过程一样,避免了海量中间数据在网路上的传输。

-- 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 setredis.call('zremrangebyscore', KEYS[1], '-inf', ARGV[1])-- Comments: add to the Ready Job list-- Comments: can optimize to use lpush id1,id2,... for better performancefor i=1,count do redis.call('lpush', KEYS[2], jobs[i])end
end

2.9 过期数据清除

官方文档 与 《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,它占用的内存什么时候最终被清理掉只有天知道。

3. 性能

3.1 测试结果

redis-benchmark -t SET -c 100 -n 10000000 -r 10000000 -d 256

3.2 为什么快

3.3 性能调优

4. 容量

4.1 最大内存

4.2 内存占用

4.4 水平分区,Sharding,Partition

Redis-Cluster在3.0版发布,支持自动re-sharding,Redis文档:集群教程

5. 高可用性

高可用性关乎系统出错时到底会丢失多少数据,多久不能服务。要综合考虑持久化,Master-Slave复制及Fail-Over配置,以及具体Crash情形,比如Master死了,但Slave没死。或者只是Redis死了,操作系统没死等等。

5.1 持久化

5.1.1 RDB文件

5.1.2 AOF文件

综上所述,RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

5.1.3 读写性能

5.1.4 性能调整

因为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的博客

5.1.5 Trouble Shooting —— Enable AOF可能导致整个Redis被Block住,在2.6.12版之前

现象描述:当AOF rewrite 15G大小的内存时,Redis整个死掉的样子,所有指令甚至包括slave发到master的ping,redis-cli info都不能被执行。

原因分析:

解决方法:
最后发现,原来是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。

5.2 Master-Slave复制

5.2.1 概述

5.2.2 slaveof执行过程,完全重用已有功能,非常经济

5.2.3 Trouble Shooting again

有时候明明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的变化。

5.3 Fail-Over

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

5.3.1 主要执行过程

5.3.2 master/slave 及 其他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信息,宣告自己的存在。

5.3.3 自定义reconfig脚本

觉得Sentinel至少有两个可提升的地方:

5.4 Client的高可用性

基于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。

5.5 Geographic Replication

依然用Master Slave复制,支持Active-Standby模式的Geographic Replication,主要用于容灾数据恢复,或者在site1倒掉时,启动备用系统指向备库。3Scale想出了诸如用压缩的SSH隧道降低传输量等方法,可以设置远端的Slave的优先级为0,则site2上的slave永远不会被选举成master,master只会在site1的slave中产生。

6. 运维

6.1 安装

6.2 部署模型

6.3 配置

约30个配置项,全都有默认配置,对redif.conf默认配置的修改见附录1。

6.3.1 三条路

6.3.2 安全保护

6.4 监控与维护

综述: Redis监控技巧

6.4.1 监控指令

Info指令将返回非常丰富的信息。 着重监控检查内存使用,是否已接近上限,used_memory是Redis申请的内存,used_memory_rss是操作系统分配给Redis的物理内存,两者之间隔着碎片,隔着Swap。 还有重点监控 AOF与RDB文件的保存情况,以及master-slave的关系。Statistic 信息还包括key命中率,所有命令的执行次数,所有client连接数量等, CONFIG RESETSTAT 可重置为0。

Monitor指令可以显示Server收到的所有指令,主要用于debug,影响性能,生产环境慎用。

SlowLog 检查慢操作(见2.性能)。

6.4.2 Trouble Shooting支持

6.4.3 持久化文件维护

6.4.4 三方工具

官网列出了如下工具,但暂时没发现会直接拿来用的:

7. Java Driver

7.1 Driver选择

各个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实例,处理异常。

7.2 Jedis的细节

Jedis基于Apache Commons Pool做的连接池,默认MaxActive最大连接数只有8,必须重新设置。而且MaxIdle也要相应增大,否则所有新建的连接用完即弃,然后会不停的重新连接。

另外Jedis设定了每30秒对所有连接执行一次ping,以发现失效的连接,这样每30秒会有一个拿不到连接的高峰。但效果如何需要独立分析。比如系统高峰之后可能有一长段时间很闲,而且Redis Server那边做了Timeout控制会把连接断掉,这时候做idle checking是有意义的,但30秒一次也太过频繁了。否则关掉它更好。

Jedis的blocking pop函数,应用执行ExecutorService.shutdownNow()中断线程时并不能把它中断,见讨论组。两个解决方法:

7.3 Redis对Client端连接的处理

8. Windows的版本

Windows版本方便对应用的本地开发调试,但Redis并没有提供,好在微软提供了一个依赖LibUV实现兼容的补丁,https://github.com/MSOpenTech/redis ,但redis作者拒绝合并到master中,微软只好苦憋的时时人工同步。 目前的稳定版是2.6版本,支持Lua脚本。

因为github现在已经没有Download服务了,所以编译好的可执行文件藏在这里:

9. 单元测试、集成测试

NoSQL Unit 是使用了Redis的项目的福音,它提供三个功能: 1. 嵌入式的Jedis实例,用于单元测试。在springside-extension的JedisTemplateTest里使用了它。 1. ManagedRedis,可控制已安装在机器上的redis,可用集成测试。将在下个迭代试用。 1. 将数据定义在json文件里,可以在测试时装载数据或校验redis中的数据。

但嵌入式的Redis,不能模仿Lua脚本。而ManagedRedis也不支持Windows上的Redis。

10. 成功案例

注:下文中的链接都是网站的架构描述文档。

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在里面扮演了八爪鱼多面手的角色:

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:

11. In SpringSide

extension modules项目封装了常用的函数与场景,showcase example的src/demo/redis目录里有各场景的benchmark测试。

11.1 Jedis Template

典型的Spring Template风格,和JdbcTemplate,HibernateTemplate一样,封装从JedisPool获取与归还Connecton的代码,有带返回值与无返回值两种返回接口。同时,对最常用的Jedis调用,直接封装了一系列方法。

11.2 Scheduler与Master Elector

Scheduler实现了基于Redis的高并发单次定时任务分发。具体选型见Scheduler章节。

Master Elector基于redis setNx()与expire()两个api实现,与基于Zookeeper,Hazelcast实现的效果类似。

11.3 Showcase中的Demo

计有Session,Counter,Scheduler 与 Master Elector四款。

12. What is new in Redis 2.8

其他用不上的new feature:

附录

附录1: 对redis.conf默认配置的修改

Master上

Slave上

附录2:版本变更历史

附录3:其他参考资料

(@江南白衣版权所有,转载请保留出处)

来源:https://github.com/springside/springside4/wiki/Redis#trouble-shooting--aofredisblock 

Redis2.8从入门到精通文档相关推荐

  1. [转]Redis2.8从入门到精通文档

    版本:V3.1 2014-3-36 (@江南白衣版权所有,转载请保留出处),针对Redis 2.8版. 1. Overview 1.1 资料 <The Little Redis Book> ...

  2. ReactJs入门教程-精华版

    原文地址:https://www.cnblogs.com/Leo_wl/p/4489197.html 阅读目录 ReactJs入门教程-精华版 回到目录 ReactJs入门教程-精华版 现在最热门的前 ...

  3. 从入门到精(fang)通(qi)第5期 我们的征途是星辰大海

    生物信息分析:从入门到精(fang)通(qi)第5期 我们的征途是星辰大海 GeneDock聚道科技 为生命计算,助看病不难! 已关注 19 人赞同了该文章 生信小白:震惊!一个全基因组会产生数百万个 ...

  4. 【转】《从入门到精通云服务器》第六讲—OpenStack基础

    前五期的<从入门到精通云服务器>受到了广泛好评,收到留言,有很多读者对云计算相关的技术非常感兴趣.应观众要求,我们这期要安利一条纯技术内容.准备好瓜子.花生,随小编一起进入OpenStac ...

  5. python做excel自动化视频教程-从零基础入门到精通用Python处理Excel数据视频教程...

    从零基础入门到精通用Python处理Excel数据视频教程 1.从零基础开始用python处理Excel数据 1-1 什么是python.mp4 1-2 为什么要学习用Python处理Excel表格. ...

  6. 小学生c语言入门ppt,C语言入门至精完整版46759.ppt

    C语言入门至精完整版46759 第10章 指针 C语言程序设计 第十章 指针 地址和指针的概念 变量的指针和指向变量的指针变量 数组与指针 字符串与指针 指向函数的指针 返回指针值的函数 指针数组和指 ...

  7. 视频教程-19全新mysql教程零基础入门实战精讲mysql视频DBA数据库视频教程SQL教程-MySQL

    19全新mysql教程零基础入门实战精讲mysql视频DBA数据库视频教程SQL教程 7年的开发架构经验,曾就职于国内一线互联网公司,开发工程师,现在是某创业公司技术负责人, 擅长语言有node/ja ...

  8. Unity_Shader,作用流程入门程精讲

    Unity_Shader 作用流程入门程精讲 Unity_Shader 渲染流水线 什么是渲染流水线 渲染流水线的各大阶段 应用阶段_程序与数据的读入 几何阶段_顶点着色器 光栅化阶段_片元着色器 简 ...

  9. ExoPlayer详解——入门(官方文档)

    ExoPlayer详解系列文章 ExoPlayer详解--入门(官方文档) ExoPlayer详解--媒体类型(官方文档) ExoPlayer详解--高级主题(官方文档) 一.ExoPlayer,你好 ...

最新文章

  1. 『ExtJS』01 001. ExtJS 4 类的定义
  2. MapXtreme 操作地图时出现调用目标发生异常的解决方法--地图状态保存
  3. Room是怎样和LiveData结合使用的?(源码分析)
  4. 一文总结知识图谱基本概念和工程落地常见问题
  5. Android还在用Toast?你Out啦,该试试Snackbar了
  6. java代码大全_各种java技术文章汇总整理
  7. snowflake 使用_如何使用机器学习模型直接从Snowflake进行预测
  8. 力扣498. 对角线遍历
  9. bzoj 2905 背单词
  10. 支付宝在线支付接口开发教程
  11. php可变方法,php 函数使用可变数量的参数方法
  12. Python抓取2500份招聘需求,数据显示未来最吃香的岗位是这个
  13. 13.卷2(进程间通信)---Posix共享内存区
  14. 微信小程序报错 TypeError: Cannot read property ‘setData‘ of undefined
  15. java并发圣经,差距不止一星半点!Github星标85K的性能优化法则圣经
  16. 【钛坦白】清华大学李建:深度学习在时空大数据分析中的应用(转载)
  17. WSAData小说明(转)
  18. android category 讲解
  19. reached getOption(“max.print”)
  20. 计算机打文档的技巧,电脑word文档下划线怎么打(word文档编辑小技巧)

热门文章

  1. Linux修改ip以及修改之后连不到网络问题
  2. vim 修改后退出命令
  3. LaTex, 矩阵方程插入 和图片表格以及公式跨栏插入
  4. vc调用python返回值为中文的处理
  5. 教你认识 阿迪达斯(ADI) 鞋标
  6. 解决qrcode生成的二维码华为手机长按不识别、toDataURL之后中间logo丢失等问题
  7. mac设置mysql root密码_mac如何更改mysql root密码 Mac平台重新设置MySQL的root密码
  8. 电脑打开回收站显示服务器运行失败,win7 64位系统无法清空回收站的故障原因及解决方法...
  9. linux系统下git本地仓库搭建及使用 (麒麟系统)
  10. 风光储、风光储并网直流微电网simulink仿真模型。 系统有光伏发电系统、风力发电系统