http://cloud.csdn.net/a/20120224/312377.html
导读:Evernote是非常著名的在线记事本应用。下面我们来一起探析它的后台架构,看看Evernote为什么没有选择流行的NoSQL。
当为大家描述我们的整体服务架构时,最常见的两个问题是:
- 为什么采用结构化方式将数据存储在SQL数据库中,而不使用NoSQL平台?
- 为什么自己维护数据中心,而不将Evernote托管到云服务提供商?
这两个问题都很有趣,我们先来探讨第一个。
对特定的应用而言,相比一个单一的SQL实例,一个现代的键值存储引擎具备显著的性能优势和可扩展性。
- CREATE TABLE notebooks (
- id int UNSIGNED NOT NULL PRIMARY KEY,
- guid binary(16) NOT NULL,
- user_id int UNSIGNED NOT NULL,
- name varchar(100) COLLATE utf8_bin NOT NULL,
- ...
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- CREATE TABLE notes (
- id int UNSIGNED NOT NULL PRIMARY KEY,
- guid binary(16) NOT NULL,
- user_id int UNSIGNED NOT NULL,
- notebook_id int UNSIGNED NOT NULL,
- title varchar(255) NOT NULL,
- ...
- FOREIGN KEY (notebook_id) REFERENCES notebooks(id)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
如果你在Windows客户端上创建了一个名为“Cooking”的记事本,并立即在其中粘贴了一个名为“Quick Tomato Sauce”的食谱,客户端会立刻进行如下同步:
- 调用NoteStore.createNotebook() 请求服务器创建记事本,并返回以创建记事本的GUID。
- 通过指定记事本的GUID,调用NoteStore.createNote()在记事本中创建笔记。
每次API调用都通过SQL事物予以实现,可以让客户端完全信任服务器的任何提示。ACID兼容的数据库可以做到这些:
- 原子性(Atomicity):如果API调用成功,那么所有的改动都会保存;如果API调用失败,所有的改动都不会提交。
- 一致性(Consistency): 在API调用完成后,所有的账户都可用,并能保证内部状态的一致性。每篇笔记都与记事本相关联,以避免出现孤立项。数据库不允许删除关联有记事的记事本,这得感谢FOREIGN KEY约束。
- 持久性(Durability):当服务器发送记事本已创建完毕的回执后,客户端会认为它的存在具有持久性,以便进行后续的操作。变更的持久性,可以让客户端知道在任何时刻对服务状态的影响都能保持一致性。
对我们的同步协议而言,持久性最为重要。如果客户端不能确定服务器端的变更具有持久性,那么协议将会变得复杂而低效。
“大数据”问题
得益于事务处理的数据库的ACID属性,同样使得数据集非常难以扩展,以超出单台服务器的范围。数据库集群和多主复制技术并不理想,键值存储为实现可扩展性提供了一条捷径。
所幸,Evernote暂时不需要考虑这个问题。即便是我们有近10亿的笔记,和近20亿的资源文件,这也并不能称得上是一个大数据集。通过按用户分区,它被划分成了2千万个独立的数据集。
我们尚未遇到所谓“大数据”引发的问题,倒是遇到了许多“中数据”的存储问题,这就是通过规整分区形成的分片存储架构。
也许以后……
我们对新的存储系统非常感兴趣,非常乐意应用在哪些对ACID要求不强,但确实需要横向扩展的新项目中。例如,我们的报告分析系统已经逐渐超出了MySQL平台的承受力,需要被更快、更先进的系统所取代。
我们现在对以Evernote用户元数据为基础的MySQL分片存储颇为满意,尽管这不会引起那些IT弄潮儿的兴趣。(张志平/编译)