Redis实践分享

介绍

Redis是REmote DIctionary Server缩写,是一个开源(BSD liscensed) 内存型key-value存储系统,常用作数据库、缓存、消息中间件。提供了一些丰富的数据结构,包括String、Lists、Hashes、Sets和Ordered Sets,以及在这些数据结构上面的操作,支持不同类型的持久化操作。并通过Redis Sentinel担供高可用性,通过Redis Cluster支持数据自动分片。

Redis Sentinel

Redis Sentinel提供了Redis集群的高可用性,提供的功能如下:
1. 监控(Monitoring)。Sentinel会一直检查Master与Slave实例是否正常运行
2. 提醒(Notification), Sentinel会通过API提醒系统管理员、其它程序,如果有监控的实例出问题。
3. 自动故障转移(Automatic failover), 如果master不工作了,Sentinel会启动一个故障转移程序将slave提升为master,其它slaves被配置使用新的master, 当应用连接时会被通知新的master地址。
4. 配置管理(Configuration provider),Sentinel作为clients的服务发现的权威提供者,clients连接到Sentinels询问关于当前与某个服务关联的Redis master。当故障发生时,Sentinels会报告新的地址。

Sentinel 本身也是一个集群,当进行失效检测时,多个实例需要对Masterr失效达成共识,降低false postives的概率。多实例Sentinel可以避免单实例Sentinel失效的问题。

特点

  • 单线程、无锁
  • 实现发布/订阅者模式
  • 所有操作均在内存中,异步方式将数据写入磁盘。
  • 用2万行ANSIC C代码,没有第三方库
  • 软件本身占用空间很少,一个空的redis在内存中只占约1M内存。
  • event demultiplexer, no use libevent
  • 主要缺点是数据库容易受到物理内存的限制,不能用作海量的高性能读写,并且它没有原生的可扩展机制,不具有scale能力,要依赖客户端来实现分布式读写民,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
  • 性能极高,Redis支持10万每秒的读写频率。
  • 所有操作都是原子的,同时Redis还支持几个操作全并后的原子执行,也即支持事务。
  • 丰富的特性,支持publish/subscribe, key过期等特性。
  • 可以为每个键值设置生存时间(Time to Live, TTL), 用于缓存系统。
  • 单个value的最大限制是1GB
  • 使用List来做FIFO双向链表,实现一个轻量级高性能消息队列服务
  • 使用Set可以做高性能的tag系统。
  • Redis采用异步复制。从Redis2.8开始,从服务器会周期性地报告从复制流中处理的数据量
  • 从服务器可以接受其他从服务器的加拉。
  • Redis的复制在主服务器上是非阻塞的。这意味着,当一个或者多个从服务器执行初始化同步时,主服务器能继续处理请求。
  • Redis的复制在从服务器上也是非阻塞的,当从服务器正在执行初始化同步时,假如你在redis.conf中进行了相应的配置,也能够继续使用旧版本的数据集中处理请求。另外,你还可以配置当复制流宕掉的时候,从服务器返回给客户端一个错误,然而,初始化同步结束后,旧的数据集需要被删除,新的数据集需要被载入。在这个极短的时间内,从服务器会阻塞到来的连接。
  • 复制可以用来支持可伸缩性,用多个从服务器处理只读查询,也可以仅仅作为数据冗余。
  • 可以使用复制来避免主服务器将全部数据集写到磁盘的开销:只需要配置你的主服务器的conf来防止保存,然后连接一个不断复制的从服务器。但是,这种设置下要确保主服务器不会自动重启。

Redis复制如何工作

当你建立一个从服务器,连接时就会发送一个SYNC命令,不管是第一次连接上还是重新连接上。
然后主服务器开始在后台保存,并且开始缓冲所有新收到的会修改数据集的命令,当后台保存完成后,主服务器传输数据库文件给从服务器,从服务器将其保存到磁盘上,然后加载到内存中。主服务器开始发送缓冲的命令给从服务器。这是通过命令流来完成的,和Redis的协议是一样的格式。

当主从链路由于某些原因断开时,从服务器可以自动重连。如果主服务器收到多个并发的从服务器的同步请求,只会执行一个后台保存来服务所有从服务器。

当主服务器和从服务器断开后重连上,总是执行一次完整的同步(full resynchronization)。然而,从redis2.8以后,可以选择执行部分同步。

部分重同步

从redis2.8开始,在复制链接断开后,主服务器和从服务器通常可以继续复制过程,而不需要一次完整的重同步。
这是通过在主服务器上创建一个复制流的内存缓冲区(in-memory backlog)实现。主服务器和所有从服务器都记录一个复制偏移量(offset)和一个主服务器运行ID(run id),当链接断掉时,从服务器会重新连接,并且请求主服务器继续复制,假设主服务器的运行ID还是一样的,并且指定的偏移量在复制缓冲敬可用,复制会从中断的点继续。如果这两个条件之一不满足,将会执行完整重同步。

新的部分重同步特性使用的是内部PSYNC命令,老的实现采用的是SYN命令。2.8的从服务器可以检测主服务器是否不支持PSYNC,然后使用SYNC代替。

从Redis2.8开始,可以设置Redis主服务器在当前至少有N个从服务器连接的情况下,才能接受写请求。

一些痛点

  • 单机内存有限
  • 带宽压力
  • 单点问题
  • 不能动态扩容
  • 磁盘损坏时数据抢救。

分片解决方案

客户端分片

把分片的逻辑放在Redis客户端实现,通过Redis客户端预定义好的路由规则,把对Key的访问转发到不同的Redis实例中。

缺点:
- 这是一种静态的分片方案,需要增加或者减少Redis实例的数量,需要手工调整分片的程序。
- 增加研发与运维沟通成本,可运维性差。
- 在不同的客户端程序中维护相同的分片逻辑成本巨大。

Twemproxy

Twemproxy是由Twitter开源的Redis代理,基本原理:Redis客户端把请求发送到Twemproy,Twemproxy根据路由规则发送到正确的Redis实例,最后Twemproxy把结果汇集返回给客户端。
业界广泛使用,久经考验、稳定性最高。

不足:
1. 性能损失
2. 没有友好的监控管理后台界面,不利于运维监控
3. 无法平滑增加Redis实例。

Codis

一个平滑增加Redis实例的Redis代理软件,基于Go和C开发,2014年11月开源。
包含下面4个部分:
- Codis Proxy: Redis客户端连接到Redis实例的代理,实现Redis的协议。无状态,可以用KeepAlived等负载均衡软件部署多个Codis Proxy实现高可用。
- CodisRedis:Codis项目维护的Redis分支,添加了slot和原子的数据迁移命令。Codis上层的Codis Proxy和Codisconfig只有与这个版本的Redis通信才能正常运行。
- CodisConfig:Codis管理工具,可以执行添加删除CodisRedis节点、添加删除CodisProxy、数据迁移等操作,自带http server集成了一个管理界面,方面运维人员观察Codis集群的状态和进行相关的操作,极大提高了运维的方便性,弥补了Twemproxy的缺点。
- Zookeeper:分布式的、开源的应用程序协调服务。Codis依赖于ZooKeeper存储数据路由表信息和Codis Proxy节点的元信息。另外,CodisConfig发起的命令都会通过 Zookeeper同步到CodisProxy的节点。

Redis 3.0集群

集群采用p2p的模式,完全去中心化,把所有的key分成了16384个slot,每个Redis实例负责其中一部分slot,集群中的所有信息都通过节点之间定期的数据交换而更新。
Redis客户端在任意一个Redis实例发出请求,如果所需要的数据不在该实例中,通过重定向命令引导客户端访问所需要的实例。

  • 一个Redis实例具备了“数据存储”和“路由重定向”功能,完全去中心化的设计,这带来的好处是部署非常简单,直接部署Redis就行。但带来的问题是很难对业务进行无痛升级,如果哪天Redis集群出现严重bug,就只能回滚整个redis集群。
  • 对协议进行较大的修改。

AOF

数据快照的缺点是持久化之后如果出现crash则会丢失一段数据,因此作者增加了另外一种追加式的操作日志记录,叫append only file, 其日志文件以aof结尾,我们一般称为aof文件,要开启aof日志的记录,需要在配置文件中进行如下设置:
appendonly yes

FAQ

  • 一般情况下cpu不会成为redis的瓶颈,内存或者网络更容易成为瓶颈。
  • 为了提高CPU利用率可以开启多个实例。
  • 可以通过命令(Config set/get)在线修改Redis配置,同时需要手动修改redis.conf,否则下次redis重启后新的配置会丢失。
  • Redis Cluster无法部署在Docker集群,如果需要则必须是--net=host模式。因为docker 会对端口进行映射,
分享到: