今天谈些什么呢,还是谈谈今天碰到的一次case吧:


在地铁上碰到一个yc,他跟我说他们碰到的一个头疼的问题,接手别人的一个存储业务,现在在线上跑着,不过redis打满了。也就是该扩容了。单端口不能扩容,只能增加端口,降低单端口的量。

怎么扩?我问他采用什么方式分片的,他告诉我hash取模。我头晕,这。。。没啥好办法,只能停服。如果不能停服,只能用双写移库的方法。


双写移库,其实是我认为一种比较low的懒惰的方法,是让别人接坑的方法。说下特点:

1. 影响接口速度,双写无疑会增加调用redis的次数,增大时间,提高错误率。

2. 周期,从4个库,移动几百G的数据,在线上 用多长时间,开着飞机换轮子是否会造成出现偏差。在之前给某业务导数据过程中,挂死过一个redis,导致线上的业务出现问题。


那么程序设计之初就要有明确的目标:

1. 运维如果能在提供固定实例端口的基础上,内容消化扩容,那么问题就好办了,也就没有以下内容。.


2. 你存储的信息是什么,根据不同的内容其实是有不同的方案的,如果存共享session这类临时数据,那么无疑一致性hash是最好的解决方案,运维增加一台机器或者减少一台机器,顶多会造成,一部分用户重新登录下,不会造成其他问题。


    而如果存储的用户信息,是不能随便增加节点和减少节点的,因为任何变动都会引起用户信息的丢失,这种丢失不可逆,不能通过重新登录解决。此时,对于你采取 一致性hash,取模,或者区间算法实际是相同的。


3. 扩容:扩容的目标应该是,解决问题而又线上业务没有问题,这个应该最高优先的目标。也就是,优化是程序架构侧的事不是产品层面的事,不应引起系统服务的波动。


我比较喜欢区间算法,理由如下:

1. 由一个简单的算法如md5 (uid) 取得最后两位,哈希到00-FF共计256个节点,假设分配如下:00-3F  40-7F 80-BF  C0-FF  分别对应4个redis实例A B C D。此时如果4个redis满了,想尽快增加端口解决问题。可以2中的步骤。


2. 对于00-3F的数据,可以摘取一个从库,单独最为主库。如A  A1, 此时A和A1中的数据是一样的。然后修改配置 00-1F到A上面, 20-3F到A1上面,这样就完成了扩充端口。

然后再写一个脚本,线下删除A里面根据算法落到20-3F的数据,A1里面根据算法落到A的数据。这样,应该会很快的删除到制定大小,无疑删除数据远大于移动写入数据的速度。


至于弊端也有,不能一步到位。只能根据步骤,分批次扩容。


总之,当你选择服务的时候,你会选择一种在你扩容的时候对线上有影响,还是一种靠线下努力,按照步骤对线上没影响的扩容方案呢。