作者:陈叶皓(携程邮轮研发部软件架构师)
我的架构师同事问我:“为什么你总说要在服务层实现读写分离,我们已经在数据库实现了读写分离,是不是已经够用”。以下是我的解释,
在做网站性能优化的时候,我常常忘记还有数据库读写分离这件事,因为数据库读写分离,对性能带来的提高太有限了,实际上,就是一倍(一台服务器变成两台服务器)。当你的网站业务发展,如果从无到有地使用数据库读写分离,提高了一倍的服务能力,你很快就需要想新的优化方案。实际上,数据库的读写分离,更像是数据安全的一个副产品,用一台数据库服务器不安全(怕数据丢失),用一台服务器作为备份,既然有了两台服务器,就充分利用吧,于是有了“读写分离”,提高一倍也是好的。
于是,能够十倍百倍提高性能的方案出现了,缓存加服务器集群,这是最常用且有效的提高网站访问量的设计。使用共享缓存(memcached,redis)可以获得十到几十倍的性能提升,使用进程内缓存,可以得到百倍的性能提升;集群中增加一倍的服务器,可以增加一倍的计算能力,服务更多的并发请求。等一下,上面所说的方案,其实只对“读”操作才有效,对“写”操作可以说是毫无用处。
那么有什么办法可以提高“写”操作的性能,在架构部署的设计方面,我的答案是,“没有”。
从硬件入手,可以使用SSD硬盘。愿意替换底层数据库,可以使用hbase或者cassandra,都不在今天讨论的范围。我想说的是,既然使用缓存和增加服务器,对于“写”操作没有优化作用,在一开始,“写”操作相关的服务,就不该和“读”操作一起,被分配到数量庞大的计算机集群里。
想象这样的架构设计,我有一个“读”服务的集群,一共4台服务器,我有一台“写”服务器(另一台备用,故障时切换)。当我的网站访问量上升,我增加“读”服务器集群到8台,简单就能应付问题。因为“读”服务是状态无关的,增加到100台也不会带来错误的数据,这是一个重要的思想,状态无关的服务,才可以放心地水平扩展,事实上,状态无关的服务,通常只有“读”服务。
那么当“写”服务撑不住的时候,怎么办,嗯。。。总会有办法,反正不是加缓存或者是使用集群,这个可以做架构师面试题。
然后我解释一下为什么不该在集群里面运行“写”服务,我把“写”服务分为两种。
1. 和“状态”(可能发生冲突的情形)弱相关,比如用户提供内容(UGC)的操作,每个用户提交自己的评论,或者发布自己的微博,不太容易发生冲突。对于这类“写”服务,部署在集群里面勉强可行,虽然没带来什么好处,但也没有引入错误
2. 和“状态”(可能发生冲突的情形)强相关,比如包含库存操作的电商网站,上千人“秒杀”热门商品,允许这样的操作在集群内并发,是架构师自己作死的节奏啊
明白了这个道理,你就知道我之前为什么说是“一台”写服务器,只有一台服务器,才可以保证在“秒杀”场景下,不会在没有库存的情况下继续售卖成功。
细心的读者(嗯,就是你)会继续追问,在一台服务器的情况下,现在都是多核并发编程,保证串行操作也不是容易的事啊。问得太好了,我这大半年写的系列文章,都是为了解决这个问题,你需要的是actor模型。异步编程加上进程内的消息队列,可以高效地对并发操作进行串行的处理。
结论,使用服务器集群提高性能只对“读”服务有效,对“写”服务无效,“写”服务器应该使用主/从模式,同一时间只使用一台服务器。在“写”服务器内部,使用支持actor模型的编程语言,保证关键操作的串行。最后老生常谈,支持actor模型的编程语言是:Erlang,Go,Scala,F#
原文链接:http://techshow.ctrip.com/archives/784.html