我司是一家正处于高速发展,目前拥有数百万用户,年销售额近五十亿的社交电商公司。

为什么使用 MongoDB(选择数据的时候我们是怎么考虑的?)
MongoDB 架构(99.99% 高可用,晚上安心睡大觉!)
MongoDB 分片(海量数据应对之道!)
MongoDB 文档模型介绍(灵活!灵活!灵活!)
为什么使用 MongoDB
安全,稳定
高可用
高性能
数据规模
支持读写并发量
延迟与吞吐量
MongoDB 架构
MongoDB 自带多副本高可用,只需要合理的配置,就能避免单数据库节点故障导致服务的不可用。

一个 Primary 主节点,主要接受来自 server 的读写。
两个 Secondary 从节点,用于同步来自 Primary 的数据。
raft 算法动画演示:
http://thesecretlivesofdata.com/raft/

上图是一个拥有 7 个可投票从节点,一个主节点,两个不可投票从节点。
{ "_id" : <num>, "host" : <hostname:port>, "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 0, // 设置为0 "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 0 // 设置为0}
既然我们的数据库拥有至少超过三个节点(1Primary+2Secondary),Secondary 通过同步 Primary 的数据来保持一致性,那么当我们写操作的时候,如何保证数据安全的落盘呢?

写 Primary 成功,返回客户端写成功,Secondary 还未同步 Primary 的时候,Primary 挂了,数据丢失!
写 Primary 成功,数据同步一个 Secondary 成功,返回客户端写成功。此时 Primary 挂了,数据不会丢失。但是恰好 Primary 与同步的 Secondary 同时挂了,数据丢失!
写 Primary 成功,数据同步两个 Secondary 成功,返回客户端写成功。此时 Primary 挂了,数据不会丢失。
第一种情况有风险会造成数据丢失。
第二种情况还是会出现数据丢失,但是数据丢失的概率大大降低。
第三种情况是最安全的做法,但是节点数目多了,同步非常耗时,用户需要等待的时间过长,一般不考虑。
MongoDB 在这里推荐折衷方案就是使用 Write Concern---在数据可靠性与效率之间的权衡!
db.products.insert( { item: "envelopes", qty : 100, type: "Clasp" }, { writeConcern: { w: "majority" , wtimeout: 5000 } } // 设置writeConcern为majority,超时时间为5000毫秒)
MongoDB 分片
查询百万表和千万表甚至过亿的表效率相差很大,查询性能急剧恶化。
插入的时候创建索引可能会引起索引树的调整与页分裂。
例如将订单库分为在线库和离线库,近三个月是在线库,远期的订单数据放入离线库,这样在线库的数据就大大减少,数据库性能就得到了提升。
又例如当我们的用户量过多超过千万行记录,单表查询效率下降,我们将一张用户表拆成多张用户表,这个就是水平拆分。

通过将同一个集合(Collection1)的数据按片键(shard keys)分到不同的分片(shard)上面,减少同一个数据文件上的数据量,已达到拆分数据规模的目的。


冷热数据,某个分片数据量过大。
数据总量大,分片集群的分片过大。
MongoDB 文档模型介绍
同一个集合中不同文档不一定需要有相同的字段,并且字段类型也可以不同。
在集合中改变文档的结构,例如增加一个字段,删除一个字段,或者改变一个字段的类型,只需要对该文档更新即可。
数据关系如下:
// patron document{ _id: "joe", name: "Joe Bookreader"}// address documents{ patron_id: "joe", // reference to patron document street: "123 Fake Street", city: "Faketon", state: "MA", zip: "12345"}{ patron_id: "joe", street: "1 Some Other Street", city: "Boston", state: "MA", zip: "12345"}
在 MongoDB 中我们可以这样进行设计:
{ "_id": "joe", "name": "Joe Bookreader", "addresses": [ { "street": "123 Fake Street", "city": "Faketon", "state": "MA", "zip": "12345" }, { "street": "1 Some Other Street", "city": "Boston", "state": "MA", "zip": "12345" } ] }
总结
作者:唐银鹏
简介:开源爱好者、Gopher。从事电商、IM 系统深度研发,MongoDB 爱好者,公众号《从菜鸟到大佬》作者。
编辑:陶家龙
出处:转载自微信公众号Mongoing 中文社区(ID:mongoing-mongoing),本文是唐银鹏在“青芒话生长”MongoDB征文比赛的获奖文章。

精彩文章推荐:




