个人思考:做了hash是否还需要读写分离

之前写了一个redis的组件。

出发点:
1.做数据源组件的一个重要的目的是负责回收连接。
2.基于@Bean注解可以做到对jedispool,jedis操作的自动装配,暴露出一个客户端。

所以最终的效果是:
1.提供了两个客户端,一个redis,一个codis。
2.支持基于zookeeper的配置管理,可以在properties中使用占位符,spring只支持在beanDefinitionParse阶段对xml文件替换占位符。所以解决办法就是把properties配置的内容声明为一个beanDefination对象注册到spring容器。
3.支持读写分离,这个用动态代理来实现。

但久而久之,开始考虑第3个功能是否有必要。
读写分离避不开的一个问题就是主从同步延迟,主库负责写,从库负责读。
在数据库上,主库把自己的binlog发送给从库,根据看到的资料,io线程是很快的,但从库把收到binlog重写到自己的数据库,是单线程,确实很慢的。虽然5.6的mysql提供了多线程的写入库。

在redis上,从库首次连接上主库时,主库开始把库中的数据写入日志文本落盘,并缓存这段时间收到的命令,写入完成后将日志发送给从库,然后再发送缓存的命令,这样就保证了主从数据一致,但同步不是实时的。

对于要求高一致性的应用,是否需要这个读写分离?
之前的认知认为从库就是用来分担主库压力,所以主库用来写,从库用来读。
但现在考虑一下,是否分担主库压力只有读写分离这一种办法?从库的主要作用是不是应该是出于数据冗余,容灾的考虑?

考虑不从数据的读写分担压力入手,考虑从数据量的水平拆分入手。
通过hash将数据拆分到多个库,单个库的访问量降低,读写在同一个库也避免的主从同步延迟。

这样达到了提供整体QPS的目的也没有引入新的问题。
目前hash常使用的有两种算法。

1.hash取余
2.一致性hash

hash取余比较简单,但扩容和缩容的时候有点麻烦,会造成之前的存储的所有记录都失效。如果是多个redis做集群的话会导致回源问题,所有的请求都会打到数据库上,这个时候数据库挂不挂就看人品了。

解决办法有两种:

1.每次扩容的机器数都是扩容前机器数的两倍,这样可以保证对hash取余时,几乎全部的数据请求的机器和之前扩容前的机器是同一台。
除了原本hash值在机器数n到2n之前的请求会回源。但一般hash值都不会这么小。

2.像codis这样,加一层,在redis实例之上一次性设置1024个slot,slot到机器的映射由proxy保证。这样即使扩容也只会影响部分slot,其余的slot正常提供服务。

一致性hash,根据其算法,扩容缩容的时候保证只会影响到当前机器的hash值大且差值最小的机器,也


就是原本会打到这个机器上的请求会转移到新加的这个机器上,只会影响到一个节点。虚拟节点也可以做到负载均衡。
比如nginx用了一致性hash。

所以,个人观点,做了hash,并不需要再使用读写分离。

update:
之前的表达存在问题。
根据场景,读多写少适合读写分离。
mysql的读写分离是为了降低写库的压力,问了我们公司DBA关于同步延时:取决于网络,和主库的操作频率,及大事务。
目前生产环境使用了读写分离,但对于有需要读写的应用都连接到了写库上,只有读的应用才代理到了读库上,绕过了读写延时的问题。
分片是最终的办法,解决的读写分离后写库依然承受不了访问压力。
对于Redis,本身在内存上操作,不会涉及IO吞吐,即使读写分离也不会提升太多性能,Redis在生产上的主要问题是考虑容量,单机最多10-20G,key太多降低redis性能,所以需要做集群,重复读写都要打到相同redis实例上,所以需要分片。

你可能感兴趣的:(Java)