惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
A
About on SuperTechFans
博客园 - Franky
Engineering at Meta
Engineering at Meta
Recent Announcements
Recent Announcements
云风的 BLOG
云风的 BLOG
B
Blog
Microsoft Security Blog
Microsoft Security Blog
L
LangChain Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
U
Unit 42
Martin Fowler
Martin Fowler
Y
Y Combinator Blog
Stack Overflow Blog
Stack Overflow Blog
博客园 - 叶小钗
Vercel News
Vercel News
Apple Machine Learning Research
Apple Machine Learning Research
The Cloudflare Blog
Last Week in AI
Last Week in AI
腾讯CDC
Microsoft Azure Blog
Microsoft Azure Blog
爱范儿
爱范儿
V
V2EX
G
Google Developers Blog

博客园 - 问天

开发笔记1: 范型 C#范型不会用,求助~ Web安全,以新浪微博“郭美美”蠕虫为例 介绍两个Python web框架:Django & Tornado 扯一下抽象 跟赵姐夫扯 webform的设计 MoSonic:对SubSonic的分布式存储、缓存改进尝试(4) MoSonic:对SubSonic的分布式存储、缓存改进尝试(2) MoSonic:对SubSonic的分布式存储、缓存改进方案尝试(1) Discuz!NT在64位Windows下运行的问题 说说分页 iPhone Web App开发杂感 Katze - 简单的.net "ORM"框架 基于Gettext的asp.net网站多语言解决方案 Django on IronPython and Windows 动态修改.Net StreamReader Encoding编码 恐怖的迅雷 TinyMCE 的音乐插件/mp3 music insert plugin 微软是如何输掉API之战(下)
MoSonic:对SubSonic的分布式存储、缓存改进尝试(3)
问天 · 2011-01-15 · via 博客园 - 问天

上文

Cache Money虽然解决了数据的读取性能瓶颈;但开发大网站数据库面临的问题远不至读压力。

首先是容量。

上千万/亿的数据量并不罕见,单一物理数据库服务器即便单纯承担写压力也会是瓶颈。更何况Cache Money仅仅是在理想状况下才可以做到数据库0读。缓存服务器更新,新增查询,复杂查询等等都还会造成读压力。

比较常见的做法是采用分表,也就是所谓的Sharding,把数据按照一定的规则,分别存储至多台数据库服务器上去。

其次是变动。

业务需求是不可预测的;无论一开始数据库表结构定义得如何完备,总会有新需求出来,需要对表结构做调整才可以实现。

数据量过了百万之后,每次对生产服务器做alter table/create index等调整都是痛苦的经历。

针对容量与变动这两个问题,FriendFeed提出的schema-less database design给出了一个相当漂亮的解决方案。

强烈推荐阅读FriendFeed的原文。

FriendFeed的方案大致是这样:

  • 只有一种表结构,只有两个列:id + blob/binary(max)
  • id本身是UUID,这本身可以很容易做sharding
  • blob可以反序列化为任意结构
  • 查询通过另外建表实现,比方说users表的blob列反序列化出来的结构中包含一个age的int属性;要查询select * from users where age = 18; 那么就另外建表如user_age,仅包括两列id / age;先查询此表获得id,再查询原本的users表获得完整数据
  • 索引表可以异步建立,而且,建立的时候它都是跟查询相关,可以根据查询条件做sharding;如上面所的age。

FriendFeed的方案相当聪明,数据本身结构及其简单,sharding很容易做。写/读压力一下子就分布出去。

blob列用于序列化(数据甚至是先zip过再存,CPU强劲,磁盘IO是瓶颈),所以结构可以随时变化;只需要保证序列化算法可以兼容不同版本即可。

而灵活的序列化,恰恰是Facebook Thrift所解决的!

(还记得一开始使用Memcached做object cache时采用了Thrift做序列化么?)

先不考虑Sharding分布方案,在MoSonic中将各个类定义为类似下面的结构:

  • id(int)
  • properties(blob)
    • user_name(varchar)
    • age(int)
    • ...
  • ...

使用时可以直接这样:User.FetchById(XXX).properties.user_name。

因为一开始已经把Thrift序列化代码生成做到SubSonic模板时,这里要给数据多增加一层结构也并非难事;有点水到渠成的感觉。

以后要修改数据结构,直接改Thrift的定义文件,然后重新生成代码就成。properties列中存的数据可能跟最新的结构不一直,但Thrift并不要求严格的匹配(BinaryFormatter则不然),它会自动忽略那些不符合的列;而一但Object被重新存入,数据就又会被重新序列化完整。

======================

FriendFeed的分布式方案要求表主建是uuid,而cache money却要求所有表必须要有自增的ID主健。

这其实不是冲突,把database_name + table_name + id看成一个uuid即可。

而FriendFeed的分布式索引,跟cache money中Vector Cache有异曲同工之妙。

都是根据查询条件做处理/sharding。

之前为MoSonic添加Vector Cache,已经需要判断查询的表名/查询条件;符合即查询缓存;这里套用FriendFeed的方案则变成,符合即查询分布式索引!

执行select id from users where age=18 limit order by id desc 0,10时

逻辑变成这样:

  1. 检查Vector Cache,存在便返回
  2. 检查分布式索引表规则,获得新的数据库连接字符串
  3. 执行查询
  4. 写入Vector Cache

插入数据时,之前仅是更新Vector Cache,现在则需多一步去插入索引表。

实际运行中,因为是先插入数据表,同步更新Vector Cache,后续的插叙已经会命中缓存;索引表的更新实质变成是备份,可以异步插入。

Thrift / Cache Money / Schema-less Database Design实际上是三个不同团队为了解决不同方面的技术问题而做出的方案,但糅合进MoSonic中时,我感到的不是冲突,而更多的是一种不谋而合的美妙。

下篇会继续讲更多一些细节。