从新浪微博看海量数据存储2014-01-0520:13:34|分类:实验报告|标签:海量存储|举报|字号订阅从新浪微博看海量数据存储一、新浪微博简介及特点(一)新浪微博简介新浪微博是一个由新浪网推出,提供微型博客服务的类Twitter网站。用户可以通过网页、WAP页面、手机客户端、手机短信、彩信发布消息或上传图片。新浪可以把微博理解为“微型博客”或者“一句话博客”。用户可以将看到的、听到的、想到的事情写成一句话,或发一张图片,通过电脑或者手机随时随地分享给朋友,一起分享、讨论;还可以关注朋友,即时看到朋友们发布的信息。(二)新浪微博的特点1.操作简单,信息发布门槛极低140个字发布信息,在文字的基础上可以同时上传图片,视频等连接。内容不限制,所见所闻,所思所想,生活琐碎和宏大主题均可发布。2.平台广泛,随时随地传播信息除了电脑以外,手机的在线发布,使得使用广泛。支持手机等平台,通过手机发布,实现了全息发布方式,真正实现了随时随地发布和接收信息。3.传播速度快,传播方式呈裂变利用各种转发,名人推荐,媒体合作等,以不同的话题和主题,博得潜在消费人群的关注。新浪微博的传播路径:一个是“粉丝路径”。A发布信息后,A的所有粉丝,都可以实时接收信息;另一个是“转发路径”。如果甲觉得A的某条微博不错,他可以“一键”转发,这条信息立即同步到甲的微博里,同时,甲的所有粉丝,又都可以实时接收信息,然后以此类推,实现“病毒式”传播。4.信息交互简便快捷除了上面所述的关注和转发功能,新浪微博还有“评论功能”、“回复功能”、“私信功能”,同时每次信息交互产生后给予用户新消息的提示,达到实时交互。这些功能为用户之间的信息交互提供了保证。作为互联网3.0时代的产品,我认为其特点的核心为“微·快”。相比1.0和2.0时代下的网站和博客论坛,微博的发布对用户要求大大降低(无论是所需技术还是时间),发布信息微小符合快速阅读下的“快餐文化”。同时“快”还体现其传播速度之快,常常短时间引发巨大的讨论。二、新浪微博数据库新浪微博的特点决定了其使用的数据库,而且不断增加的用户数和访问量也导致其架构和数据库上的改变。(一)MySQL数据库新浪微博这个产品从架构上来分析,它需要解决的是发表和订阅的问题。最开始采用的是推的消息模式。新浪微博首席架构师杨卫华是这么描述的“假如说我们一个明星用户他有10万个粉丝,那就是说用户发表一条微博的时候,我们把这个微博消息攒成10万份,这样就是很简单了,第一版的架构实际上就是这两行字。”第一版的技术细节,典型的LAMP架构(Linux作为操作系统,Apache和Nginx作为Web服务器,MySQL作为数据库,PHP/Perl/Python作为服务器端脚本解释器),是使用Myisam搜索引擎,它的优点就是速度非常快。后来随着用户越来越多,访问量剧增。每时每刻都有大量的信息被发布,最初的架构就有延迟的问题,而用户需要的是根据关注关系实时投递。后来就有了分片(分库分表)、拆分、异步处理和分布式系统。1.sharding(分片)Sharding(分片)只用于数据量大同时有性能瓶颈的库,大部分库不进行sharding处理。对于数据量比较大的库,在一开始就考虑sharding策略,例如索引数据和内容数据分开设计,每类数据库根据业务逻辑选择恰当的partitioningkey,拆分成一定数量的表。(见图一)然后随着压力的增加进行垂直拆分,垂直拆分后的库再遇到性能瓶颈时首先考虑用硬件来解决。当硬件解决不了时才开始考虑水平拆分。在选择sharding方案时仔细考虑业务逻辑。对于读密集型应用,基本上通过增加slave来解决,对于写密集型应用才进行垂直和水平拆分工作。(图二)图一按不同partitioningkey进行拆分图二master/slave的拆分跨越越多的sharding,带来的开销就越大,这个数量是如何控制的?新浪首席DBA杨海潮这样回答的“目前我在设计之前就避免跨表操作,选择适当的paritioningkey,也即合适的拆分维度,避免对后期业务的影响。根据业务逻辑的重要程度,如果业务逻辑是查询某一个用户的信息,那么会按用户进行拆分,那么保证一个用户的数据是落在一张表里面。按时间维度进行拆分,那么会分析数据的冷热程度,把80%以上的数据放在一个表,避免过多的跨表查询。在这种拆分维度满足不了业务需求时,我们会利用空间换时间的思想,同一份数据按多种维度进行拆分,让每种业务逻辑的查询语句都有很高的效率。”关于sharding(分片)和partitioning(划分),sharding通常是指垂直拆分和水平拆分,是一个总体的概念,mysql的partitioning是实现sharding的一种技术。同时杨海潮认为Sharding的原则有以下几点:一开始就关注架构设计;Scaleup--Scaleout--Scaleup;成本可控下硬件是首选;逐步解决拆分中成本问题。2.异步处理异步处理,发布是一个非常繁重的操作,它要入库、统计索引、进入后台,如果要把所有的索引都做完用户需要前端等待很长的时间,如果有一个环节失败的话,用户得到的提示是发表失败,但是入库已经成功。所以新浪微博做了一个异步操作,就是发表成功就提示成功,然后在后台慢慢的消息队列慢慢的做完。另外新浪发表了一个很重要的产品叫做MemcacheQ,一个单纯的分布式消息队列服务。3.分布式系统第一个是Master/Slave(图二),但是它有两个缺点,第一个是Master是中心化的,如果Master在北京那广州访问就非常慢。第二种就是Multi-Master方案,它需要应用避免冲突,就是不能多处改变。这个对于微博来说不会特别难,用户通常只会再一个地方发布微博,很难既在广州又在北京发表或者是修改自己的资料。第三个就是Paxos就是可以达到强一致写,就是一条数据如果成功肯定是多个机房都成功了,这个也显而易见就是延迟性非常大。因此总结一下Multi-Master是最成熟的策略,但是MySQL只支持一对多的主从复制,而不支持多主(multi-master)复制。(二)NoSQL数据库随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。Redis是一个基于内存的Key-Value存储的NoSQL引擎。与其他Key-Value引擎不同,Redis的Value可以支持多种数据结构,如哈希、List、Set等。可以通过RBR解析BINLOG将MySqL同步到redis。Redis提供特定数据结构的读写操作,实现关系型数据转变成队列数据和通过replication接口同时写入到MySQL。用解决rdb和aof模式解决replicaton问题,以及加入position思想来解决容灾问题。但是一直以来,Redis的持久化方案并非完美。从rdb到aof,再到已经夭折的vm,Redis的持久化之路一直走的很坎坷。由于持久化的不完美,以及全部在内存中处理数据的特性,使得Redis在面对数据量持续增长的时候,总体拥有成本(TCO)也随之线性增长。新浪微博开放平台资深工程师唐福林表示,新浪微博从2010年底开始使用Redis,各项业务指标在经历了2011年全年的疯狂增长之后,他们发现在很多场合Redis已经不再适用。唐福林认为,Redis适用于数据量不太大的存储,以及数据量大的缓存。在选择数据存储介质的时候要分清数据量的大小和数据的冷热。小而热的数据适合使用内存,大而冷的数据适合使用磁盘,大而热的数据是否适合使用SSD,仍待探讨。(三)MySQL+NoSQL新浪微博现在采用SQL+NoSQL结合的数据库部署,根据应用的特点选择合适存储方式。譬如:关系型数据,例如:索引使用MySQL存储,非关系数据库,例如:一些K/V需求的,对并发要求比较高的放入NoSQL产品存储,或者通过关系数据复制到NoSQL(redis)来显示不同的应用需求。针对MySQL做的优化比较多,从硬件(使用SSD,Fusion-IO,Cachecade等),文件系统(尝试XFS),调整IO调度,优化参数,调整索引到减少应用对数据库的访问和交换等。NoSQL(redis)通过修改源码满足自己的业务需求:完善它的replication机制,加入position的概念,让维护更容易,同时failover能力也大大增强。改善Hashset在rdb里面的存储方式,提升复杂数据类型的加载速度。另外通过php+mysql+memcached可以来模拟一个非关系型数据库(图三),以解决数据量增加,传统数据库难以解决的问题。图三MySQL+MemcachedMySql复制延迟、慢查询,另外就是热门事件,比如说刚刚到来的2014年,跨年的时候2014年第一分钟,新浪微博的发布量以808298条再次刷新记录,第一秒微博发布量相较去年提升55%。如何来处理这样大的数据量?MySQL和和Cache结合来实现高并发的读写服务,同时减少应用开发复杂度,以及使用WTWR的实现思想来处理数据库的读/写扩展性。应对Cache层失效带来的高并发读以及瞬时写入高峰问题,杨海潮认为Caching原则有四个:采用一致性Hash部署;Cache按照冷热分层;所有热数据都放入Cache;双写来避免雪崩问题;队列方式持久化落入MySQL。所以在像新浪微博这样海量数据需要存储的时候,要有持续的数据库分片,使用缓存数据库,关系型和非关系型结合使用。当然硬件也要提升。三、新浪微博数据库设计这里用MySQL进行一个对数据库、缓存、前台页面和发微博进行简单的描述。(一)数据库CREATETABLE`comment`(`id`BIGINT(20)NOTNULLAUTO_INCREMENT,`user_id`INT(11)NOTNULLDEFAULT'0'COMMENT'评论者id',`reply_comment_id`BIGINT(20)NOTNULLDEFAULT'0'COMMENT'回复的评论id',`content`VARCHAR(255)NOTNULLDEFAULT''COMMENT'评论内容',PRIMARYKEY(`id`),KEY`user_id`(`user_id`))ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='评论'关于这个一些猜想:微博的用户关系数据量量大,应该是分库分表存储的。所以关注用户和粉丝用户设计成两张表,用空间换时间保证查询效率,每张表上缓存了双向关注的标记。好友发的消息就会出现在主页里,这种方式确实很人性化,但是就引出了一个疑问,他们应该如何组织数据库才能在很大的数据量和访问量下效率最高呢?首先,数据库应该是这样的:(括号里是字段,其他无关信息省略)用户表:user(uid,name),uid为主键。信息表:msg(mid,uid,content),mid为主键,uid为发贴人。好友表:friend(uid,fid),uid为主人,fid为好友(即uid订阅的人的id)。这样,要查询出某人的所有好友的信息就可以这样写SQL语句:(已查询uid=1的为例)1)SELECT*FROMmsg,friendWHEREmsg.uid=friend.fidANDfriend.fid=1;2)SELECT*FROMmsgWHEREuidIN(SELECTfidFROMfriendWHEREuid=1);子查询的方法比级联两张表慢了不止1个数量级。(测试结果,方法1)能在10-30ms查询出结果,而方法2)则用了3min还没有出来结果)再增加一张中介表可以提高速度:中介表:linker(uid,mid)中介表是这样用的,每次当一个人发了一篇帖子,则将所有订阅他的人(uid)和这篇帖子的mid插入中介表里,例如,uid=1的人有3个订阅者,分别为:2、3、4,则当uid=1的人发了一