为什么要在服务层设计读写分离

Posted 程序文刀刘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么要在服务层设计读写分离相关的知识,希望对你有一定的参考价值。

先对上一篇比较erlang与go语言的话题作一个补充,我的一个架构师同事写了一段简单的累加代码测试erlang密集计算的性能,测下来erlang的计算性能大概是c#的1/10,这个符合我的预期,动态类型语言和静态类型语言的性能,差不多就是差10倍,所以,go语言还是大有可为的(go语言比python快15倍)。

 

我经常喜欢和朋友聊软件技术的话题,如果觉得还有点意思,怕自己忘记,就应该记下来,就像我现在正在做的,人就是在自我否定中前行,所以我说的,有可能都是错的。

 

我的架构师同事问我:“为什么你总说要在服务层实现读写分离,我们已经在数据库实现了读写分离,是不是已经够用”。以下是我的解释,

 

在做网站性能优化的时候,我常常忘记还有数据库读写分离这件事,因为数据库读写分离,对性能带来的提高太有限了,实际上,就是一倍(一台服务器变成两台服务器)。当你的网站业务发展,如果从无到有地使用数据库读写分离,提高了一倍的服务能力,你很快就需要想新的优化方案。实际上,数据库的读写分离,更像是数据安全的一个副产品,用一台数据库服务器不安全(怕数据丢失),用一台服务器作为备份,既然有了两台服务器,就充分利用吧,于是有了“读写分离”,提高一倍也是好的。

 

于是,能够十倍百倍提高性能的方案出现了,缓存加服务器集群,这是最常用且有效的提高网站访问量的设计。使用共享缓存(memcached,redis)可以获得十到几十倍的性能提升,使用进程内缓存,可以得到百倍的性能提升;集群中增加一倍的服务器,可以增加一倍的计算能力,服务更多的并发请求。等一下,上面所说的方案,其实只对“读”操作才有效,对“写”操作可以说是毫无用处。

 

那么有什么办法可以提高“写”操作的性能,在架构部署的设计方面,我的答案是,“没有”。

 

从硬件入手,可以使用SSD硬盘。愿意替换底层数据库,可以使用hbase或者cassandra,都不在今天讨论的范围。我想说的是,既然使用缓存和增加服务器,对于“写”操作没有优化作用,在一开始,“写”操作相关的服务,就不该和“读”操作一起,被分配到数量庞大的计算机集群里。

 

想象这样的架构设计,我有一个“读”服务的集群,一共4台服务器,我有一台“写”服务器(另一台备用,故障时切换)。当我的网站访问量上升,我增加“读”服务器集群到8台,简单就能应付问题。因为“读”服务是状态无关的,增加到100台也不会带来错误的数据,这是一个重要的思想,状态无关的服务,才可以放心地水平扩展,事实上,状态无关的服务,通常只有“读”服务。

那么当“写”服务撑不住的时候,怎么办,嗯。。。总会有办法,反正不是加缓存或者是使用集群,这个可以做架构师面试题。

 

然后我解释一下为什么不该在集群里面运行“写”服务,我把“写”服务分为两种。

1.       和“状态”(可能发生冲突的情形)弱相关,比如用户提供内容(UGC)的操作,每个用户提交自己的评论,或者发布自己的微博,不太容易发生冲突。对于这类“写”服务,部署在集群里面勉强可行,虽然没带来什么好处,但也没有引入错误

2.       和“状态”(可能发生冲突的情形)强相关,比如包含库存操作的电商网站,上千人“秒杀”热门商品,允许这样的操作在集群内并发,是架构师自己作死的节奏啊

 

明白了这个道理,你就知道我之前为什么说是“一台”写服务器,只有一台服务器,才可以保证在“秒杀”场景下,不会在没有库存的情况下继续售卖成功。

 

细心的读者(嗯,就是你)会继续追问,在一台服务器的情况下,现在都是多核并发编程,保证串行操作也不是容易的事啊。问得太好了,我这大半年写的系列文章,都是为了解决这个问题,你需要的是actor模型。异步编程加上进程内的消息队列,可以高效地对并发操作进行串行的处理。

结论,使用服务器集群提高性能只对“读”服务有效,对“写”服务无效,“写”服务器应该使用主/从模式,同一时间只使用一台服务器。在“写”服务器内部,使用支持actor模型的编程语言,保证关键操作的串行。最后老生常谈,支持actor模型的编程语言是:Erlang,Go,Scala,F#




以上是关于为什么要在服务层设计读写分离的主要内容,如果未能解决你的问题,请参考以下文章

服务读写分离架构,绝不推荐

分布式数据层中间件详解:如何实现分库分表+动态数据源+读写分离

MySQL主从复制与读写分离

MySQL 主从复制与读写分离(原理深刻,过程详细,值得一看)

详解MySQL读写分离

企业级应用,持久层架构方案三(一主多从,读写分离上篇)