概述
一般来说,要将Redis运用于工程项目中,只使用一台redis是万万不能的,原因如下:
- 从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
- 从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内容容量为256G,也不能将所有内容用作Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。
本文先讨论第一点的解决方案:Redis主从复制,第二点可以使用Redis集群解决,下一篇文章将介绍Redis集群。
主从复制
考虑如下一种场景:
电子商务网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是”多读少写”。
对于这种场景,我们可以使如下这种架构:
如图中所示,我们将一台Redis服务器作主库(Matser),其他三台作为从库(Slave),主库只负责写数据,每次有数据更新都将更新的数据同步到它所有的从库,而从库只负责读数据。这样一来,就有了两个好处:
- 读写分离,不仅可以提高服务器的负载能力,并且可以根据读请求的规模自由增加或者减少从库的数量,棒极了;
- 数据被复制成了了好几份,就算有一台机器出现故障,也可以使用其他机器的数据快速恢复。
需要注意的是:在Redis主从模式中,一台主库可以拥有多个从库,但是一个从库只能隶属于一个主库。
配置
在Redis中,要实现主从复制架构非常简单,只需要在从数据库的配置文件中加上如下命令即可:
1
| slaveof 主数据库地址 主数据库端口
|
主数据库不需要任何配置。
示例
下面将演示怎么实现一个简单的复制系统。我们在一台机器上起两个Redis实例,监听不同的端口,其中一个作为主库,另外一个作为从库。首先不加任何参数来启动一个Redis实例作为主数据库:
可以看到,主库监听的是6379端口。
然后加上slaveof参数启动另一个Redis实例作为从库,并且监听6380端口:
从控制台输出中可以看到,从库已经连接到主库:126.0.0.1:6379了,看样子主从复制系统配置成功。我们可以分别在主库和从库中使用如下命令看一看当前实例在复制系统中的相关信息:
接下来验证一把。
首先在主库中设置一个键值:
1 2 3 4 | [qifuguang@Mac~]$ /opt/soft/redis-3.0.4/src/redis-cli -p 6379 127.0.0.1:6379> set test-sync winwill2012 OK 127.0.0.1:6379> |
现在到从库中检查该值是否已经自动同步到了从库:
1 2 3 4 | [qifuguang@Mac~]$ /opt/soft/redis-3.0.4/src/redis-cli -p 6380 127.0.0.1:6380> get test-sync "winwill2012" 127.0.0.1:6380> |
可以看到,数据确实从主库同步到了从库.
在默认情况下,从库是只读的,如果在从库中写数据将会报错:
1 2 3 4 | [qifuguang@Mac~]$ /opt/soft/redis-3.0.4/src/redis-cli -p 6380 127.0.0.1:6380> set x y (error) READONLY You can't write against a read only slave. 127.0.0.1:6380> |
但是可以在从库的配置文件中加上如下的配置项允许从库写数据:
1
| slave-read-only no
|
但是,因为从库中修改的数据不会被同步到任何其他数据库,并且一旦主库修改了数据,从库的数据就会因为自动同步被覆盖,所以一般情况下,不建议将从库设置为可写。
相同的道理,配置多台从库也使用相同的方法,都在从库的配置文件中加上slaveof参数即可。
此外,我们可以在客户端使用命令
1
| SLAVEOF 新主库地址 新主库端口
|
来修改当前数据库的主库,如果当前数据库已经是其他库的从库, 则当前数据库会停止和原来的数据库的同步而和新的数据库同步。
最后,从数据库还可以通过运行命令:
1
| SLAVEOF NO ONE
|
来停止接受来自其他数据库的同步而升级成为主库。
原理
上面说了配置主从复制系统的方法,并且举例例子详细说明,本节将介绍Redis主从复制的实现原理。
当一个从数据库启动时,会向主数据库发送SYNC命令,主数据库收到命令后会开始在后台保存快照(即RDB持久化过程),并将保存快照期间接收到的命令缓存起来。当快照完成后,Redis会将快照文件和缓存的命令发给从数据库,从数据库收到数据后,会载入快照文件并执行缓存的命令。以上过程称为复制初始化。复制初始化之结束后,主数据库每收到写命令时就会将命令同步给从数据库,从而保证主从数据库数据一致,这一过程称为复制同步阶段。
有两点需要注意:
- 当主从数据库之间的连接断开后,Redis2.8之前的版本会重新进行复制初始化过程,这样就使得主从数据库断开连接后数据恢复的过程的效率很低下。Redis2.8版本的一个重要改进就是断线支持有条件的增量数据传输,当从数据库再次连接到主数据库时,主数据库只需要将断线期间执行的命令发给从数据库即可,大大提高了Redis主从复制的实用性。
- 复制同步阶段贯穿整个主从同步过程的始终,直到主从关系终止为止。在复制过程中,即使关闭了RDB方式的持久化(删除所有save参数),依旧会执行快照操作。
乐观复制
Redis采用了复制的策略。容忍在一定时间内主从数据库的内容是不同的,但是两者的数据最终会保持一致。具体来说,Redis主从数据库之间的复制数据的过程本身是异步的,这意味着,主数据库执行完客户端的写请求后会立即将命令在主数据库的执行结果返回给客户端,而不会等待从数据库收到该命令后再返回给客户端。这一特性保证了复制后主从数据库的性能不会受到影响,但另一方面也会产生一个主从数据库数据不一致的时间窗口,当主数据库执行一条写命令之后,主数据库的数据已经发生变动,然而在主数据库将该命令传送给从数据库之前,如果两个数据库之间的连接断开了,此时二者间的数据就不一致了。从这个角度看,主数据库无法得知命令最终同步给了几个从数据库,不过Redis提供了两个配置选项来限制只有至少同步给指定数量的数据库时,主数据库才是可写的:
1 2 | min-slaves-to-write 3 min-slave2-max-lag 10 |
第一个参数表示只有当3个或3个以上的从数据库连接到主库时,主数据库才是可写的,否则返回错误。
第二个参数表示允许从数据库失去连接的最长时间,该选项默认是关闭的,在分布式系统中,打开并合理配置该选项可以降低主从架构因为网络分区导致的数据不一致问题。
图结构
从数据库不仅可以接收主数据库的数据,同时也可以作为主数据库存在,形成类似图的结构,如下图:
A中的数据会同步到B,C中,C中的数据会同步到D,E中。