前言:Redis是一种开源的内存数据结构存储系统,它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等。它可以用作数据库、缓存和消息中间件,并在性能、可扩展性和灵活性方面表现出色。
🦙(工作遇到傻逼,千万要远离。道路千万条,远离傻逼第一条)
1.背景介绍
Redis
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,通俗的来讲就是基于内存的高性能K/V数据库。
Redis 作为一个key—value储存系统。支持储存的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希散列)。
Redis的内核是用标准ANSI C写成的,基于一种事件模型;
高速,数据存在内存中,在内存中进行操作;
持久化,可以异步储存数据到硬盘中,在宕机恢复后迅速解决数据丢失的问题;
分布式 读写分离模式;
单线程,利用redis队列技术并将访问变为串行访问,消除了传统数据库串行控制的开销;
属于NoSql,支持事务,操作都是原子性;
可用作cache,消息总线,或者在某些开发项目中作为结构不复杂的数据库来使用
redis的性能如何?
每秒可以处理超过10万次读写操作
机械硬盘的读写速度:50-90MB/s,
固态硬盘的读写速度可以达到:500MB/s,
内存DDR3 1333Hz的读写速度大概在8G/s,
新浪微博架构师杨卫华(11年)曾说过:“国内前十大网站的子产品估计用1台Redis就可以满足存储及Cache的需求”。
redis的作者叫Salvatore Sanfilippo,来自意大利的西西里岛,现在居住在卡塔尼亚。网名是antirez。
目前antirez本人在为VMWare 公司工作(https://www.vmware.com/cn.html),主要就是进行Redis的开发。 他的个人介绍:http://www.invece.org/
2.知识剖析
集群(CLUSTER)
集群是一组相互独立的、通过高速网络互相联通的节点,构成了一个组,并以单一系统的模式加以管理。一个客户与集群相互作用时,集群就是一个独立的服务器。
集群技术是一种通用的技术,其目的是为了解决单机运算能力的不足、IO能力的不足、提高服务的可靠性、获得规模可扩展能力,降低整体方案的运维成本(运行、升级、维护成本)。能在大流量访问下提供稳定的业务,集群化是存储的必然形态。
提高性能
降低成本
提高可扩展性
增强可靠性
Redis集群(redis-cluster)
Redis 集群是一个提供在多个Redis节点之间共享数据的程序集。
Redis 集群并不支持同时处理多个键的 Redis 命令,因为这需要在多个节点间移动数据,这样会降低redis集群的性能,在高负载的情况下可能会导致不可预料的错误。
Redis 集群通过分区来提供一定程度的可用性,即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
Redis 集群的优势:
1.缓存永不宕机:启动集群,永远让集群的一部分起作用。主节点失效了子节点能迅速改变角色成为主节点,整个集群的部分节点失败或者不可达的情况下能够继续处理命令;
2.迅速恢复数据:持久化数据,能在宕机后迅速解决数据丢失的问题;
3.Redis可以使用所有机器的内存,变相扩展性能;
4.使Redis的计算能力通过简单地增加服务器得到成倍提升,Redis的网络带宽也会随着计算机和网卡的增加而成倍增长;
5.Redis集群没有中心节点,不会因为某个节点成为整个集群的性能瓶颈;
6.异步处理数据,实现快速读写。
Redis 集群的数据分片
Redis 集群没有使用一致性hash,而是引入了哈希槽的概念。
Redis 集群内置了16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:
节点 A 包含 0 到 5500号哈希槽.
节点 B 包含5501 到 11000 号哈希槽.
节点 C 包含11001 到 16384号哈希槽.
这种结构很容易添加或者删除节点.
比如如果我想新添加个节点D,我需要从节点 A, B, C中得部分槽到D上.
如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可.
由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
Redis 集群的主从复制模型
为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,每个节点都会有N-1个复制品.
比如之前的例子有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.
然而如果在集群创建的时候(或者过一段时间)我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了。不过当B和B1 都失败后,集群就不可用了。
主从复制的一些特点
1)采用异步复制;
2)一个主redis可以含有多个从redis;
3)每个从redis可以接收来自其他从redis服务器的连接;
4)主从复制对于主redis服务器来说是非阻塞的,这意味着当从服务器在进行主从复制同步过程中,主redis仍然可以处理外界的访问请求;
5)主从复制对于从redis服务器来说也是非阻塞的,这意味着,即使从redis在进行主从复制过程中也可以接受外界的查询请求,只不过这时候从redis返回的是以前老的数据,如果你不想这样,那么在启动redis时,可以在配置文件中进行设置,那么从redis在复制同步过程中来自外界的查询请求都会返回错误给客户端;
(虽然说主从复制过程中对于从redis是非阻塞的,但是当从redis从主redis同步过来最新的数据后还需要将新数据加载到内存中,在加载到内存的过程中是阻塞的,在这段时间内的请求将会被阻,但是即使对于大数据集,加载到内存的时间也是比较多的);
6)主从复制提高了redis服务的扩展性,避免单个redis服务器的读写访问压力过大的问题,同时也可以给为数据备份及冗余提供一种解决方案;
7)为了编码主redis服务器写磁盘压力带来的开销,可以配置让主redis不在将数据持久化到磁盘,而是通过连接让一个配置的从redis服务器及时的将相关数据持久化到磁盘,不过这样会存在一个问题,就是主redis服务器一旦重启,因为主redis服务器数据为空,这时候通过主从同步可能导致从redis服务器上的数据也被清空;
3.常见问题
集群节点的操作方式:
集群重新分片
./redis-trib.rb reshard 127.0.0.1:7000
添加一个新节点
./redis-trib.rb add-node 127.0.0.1:7006
添加一个从节点
./redis-trib.rb add-node --slave 127.0.0.1:7006
移除一个节点(如果是主节点要确保这个主节点是空的)
./redis-trib del-node 127.0.0.1:7000 <\node-id>
迁移从节点
CLUSTER REPLICATE <\master-node-id>
停止节点
./redis1/redis-cli -p 7001 shutdown
集群方式登录
./redis-cli -c -h -p 如:./redis-cli -p 7003 -c 默认情况下不能从slaves读取数据,但建立连接后,执行一次命令READONLY,该slaves即可读取数据。
Redis 持久化
redis提供了不同级别的持久化方式,一种是RDB,一种AOF。
可以同时开启两种持久化方式, 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.
–
RDB:在指定的时间间隔能对数据进行快照存储(就是隔一段时间把内存里的数据转存到硬盘等介质上)
–
AOF:每次对服务器写的操作指令记录下来,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.
4.编码实战
Redis集群实现方式
*参考页首配置步骤
1.创建不同的redis节点,在需要的地方运行redis实例,每个redis实例有单独的ip或端口,并且配置启用集群管理;
2.创建redis-trib的运行环境:安装ruby,redis.gem
3.通过使用Redis集群命令工具redis-trib创建集群;
./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002
–replicas 1 表示 自动为每一个master节点分配一个slave节点,上面有6个节点,程序会按照一定规则生成3个master(主)3个slave(从)
redis.conf 集群的基础配置
绑定ip(这里是本地配置,其他服务器配置对应的ip)
bind 127.0.0.1
端口
port 6379
后台守护启动
daemonize yes
开启aof模式
appendonly yes
开启集群管理
cluster-enabled yes
节点响应超时时间 如果超过这个响应时间就会认定节点不可用
cluster-node-timeout 5000
5.扩展思考
Redis 一致性保证
Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作.
第一个原因是因为集群是用了异步复制和写入的操作过程:
客户端向主节点B写入一条命令;
主节点B向客户端回复命令状态;
主节点将写入操作复制给他的从节点 B1, B2 和 B3;
这时候主节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。
注意:Redis 集群可能会在将来提供同步写的方法。
另外一种可能会丢失命令的情况是集群出现了网络分区, 并且一个客户端与至少包括一个主节点在内的少数实例被孤立。
举个例子 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六个节点, 其中 A 、B 、C 为主节点, A1 、B1 、C1 为A,B,C的从节点, 还有一个客户端 Z1 假设集群中发生网络分区,那么集群可能会分为两方,大部分的一方包含节点 A 、C 、A1 、B1 和 C1 ,小部分的一方则包含节点 B 和客户端 Z1 . Z1仍然能够向主节点B中写入, 如果网络分区发生时间较短,那么集群将会继续正常运作,如果分区的时间足够让大部分的一方将B1选举为新的master,那么Z1写入B中得数据便丢失了.
不同方式的redis的集群化方案
(1)Twitter开发的twemproxy代理方案
(2)豌豆荚开发的codis
(3)redis官方的redis-cluster
(4)客户端分片
(5)Sentinel哨兵模式
客户端分区
就是在客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取。大多数客户端已经实现了客户端分
区。
代理分区
意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些Redis实例,然后根据Redis的响应结果返回给客户端。redis和memcached的一种代理实现就是Twemproxy
查询路由(Query routing)
意思是客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点。
持久化数据还是缓存?
无论是把Redis当做持久化的数据存储还是当作一个缓存,从分区的角度来看是没有区别的。当把Redis当做一个持久化的存储(服务)时,一个key必须严格地每次被映射到同一个Redis实例。当把Redis当做一个缓存(服务)时,即使Redis的其中一个节点不可用而把请求转给另外一个Redis实例,也不对我们的系统产生什么影响,我们可用任意的规则更改映射,进而提高系统的高可用(即系统的响应能力)。
一致性哈希能够实现当一个key的首选的节点不可用时切换至其他节点。同样地,如果你增加了一个新节点,立刻就会有新的key被分配至这个新节点。
重要结论如下:
如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容,也就是说可以动态扩展操作节点
如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样