这两天,有一篇很技术的博客《微博计数器的设计12》,逐步介绍了大并发、大数据量时,sina微博计数器的实现原理,详情请 移步原文欣赏。

作为一个架构初学者,看到很多似曾相识的名词和未透彻的技术,这里对名词进行解释,对每一种设计方式尝试剖析。

使用MySql数据库实现计数器

这种是最简单的方法,可能的表设计是:

create table weibo_ext(

weibo_id bigint not null,

repost_num integer default 0,

comment_num integer default 0,

UNIQUE KEY `weibo_id_idx` (weibo_id)

) ENGINE=innodb;

当数据量在百万甚至千万时,单表都还可能支撑。(to myself:这里,如果能给出预估的并发量就更好了)但是,由于微博的转发、评论数变化较快,即读写频率都很高,所以可能会出现读写锁竞争,导致访问变慢。

当数据量到达500w时,就得为分表做准备了,如原文所述:

  1. 按id取模,把数据拆分到N个表当中去
  2. 按id的时间来分段拆表,满了就建新表

第一种方法,比如我们建了8张weibo_ext_N表,结构都与weibo_ext一致。在代码端,weibo_id%8依次分散到各个表进行读写。这样当读写一条微博时,表较小会较快。批量查询的话,则可能要做1-8次表查询。更严重的情况是,当所有表都膨胀到千万数据量时,再添加新表会很痛苦,几乎所有数据都需要重新散列到各表中。

有没有更好的散列方法了呢?分布式应用中,有一种叫一致性hash算法。上面取模仅仅散列了key,而一致性hash还会额外散列服务器,两种散列方式需要保证数据空间一致,从而使散列后每个服务器节点负责一片散列空间里的数据。当新增服务器时,只会影响到原先一台服务器的一部分数据。针对一致性hash,还有一种升级方案,即将每个真实的服务器实为多个虚拟服务器,并均匀散列到数据空间里。采用这种方式,对mysql按id随机的分表,会有一定帮助。

第二种方法,是按id区间分表,很简单,不重复。

这两种方法,千亿条微博需要生成上千张表,为了稳定性和效率还需要做一主多从的数据库,成本较高。

使用Mysql+memcached实现计数器

假设采取了mysql实现计数器的方案,并姑且不考虑存储成本,但是当访问量提升之后,Mysql还是会成为性能瓶颈,尤其是写操作也很频繁时,数据库缓存可能频繁失效,也有可能发生读写锁竞争使访问堆积,再引起mysql connection不足的问题,进而导致web server响应缓慢、可用进程数不足,使前端用户感觉到页面刷新慢甚至无法打开。

那么惯性思维,我们会想到添加memcached之类的缓存,来减轻mysql的负担。由于需要保证计数的原子性,即转发、评论数的增加需要在缓存内部完成,而非先get再set,可能会将转发和评论数分表存储,即weibo_id=>repost_num和weibo_id=>comment_num两条。如原文所述,有两个弊端:空数据也得Cache浪费内存, Cache频繁失效。针对cache频繁失效,还需要考虑,是超时失效,还是每次写操作都强制delete使失效呢?前者会造成数据不一致,后者会增加写操作的复杂度。

这种方式,不但要接受mysql的存储成本,还增加了memcached的成本。

结合牛B硬件实现计数器

我对硬件没什么了解,只能google到一些资料。猜测其实现方式是:

  1. mysql作为持久化存储,但是不使用传统的sql访问方式,而是handlesocket,免去了sql优化解析等步骤,直接操作数据,提高速度
  2. 固态硬盘,提高硬盘读写效率
  3. cache缓存访问量高的数据

我一向小成本开发,不太了解这种方案会消耗多少money、提速到何种程度?

学习《sina微博计数器的设计》二

Leave a Reply