电商参考架构

[搭建灵活-可搜索-响应快速的产品目录系统][1]

大数据量电商如何使用MongoDB作为一个庞大产品目录持久层的一些最佳实践
多功能商品目录系统的建模和索引的最佳实践,包括商品及商品系列的查询、店铺价格以及支持多样化搜索的目录浏览

  • 对商品的搜索
  • 对商品系列的搜索
  • 对商品在每个店铺价格的检索
  • 允许目录的多方面搜索和浏览

商品数据模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//definition: db.product, 只展示了对每件商品而言最重要的信息,例如类别、品牌以及描述:
{
“_id”: “30671”, //main item ID
“department”: “Shoes”,
“category”: “Shoes/Women/Pumps”,
“brand”: “Calvin Klein”,
“thumbnail”: “http://cdn.../pump.jpg”,
“title”: “Evening Platform Pumps”,
“description”: “Perfect for a casual night out or a formal event.”,
“style”: “Designer”,

}
//find by ID
db.product.findOne({_id:"30671"});
//find by serias IDs
db.product.findOne({_id:{$in:["30617","45212"]}}); //$in operator
//find by
db.product.findOne({category:/^Shoes/Women/}); // regular express

系列数据模型

对产品目录而言另一个重要的考量是商品系列,例如现有尺寸、颜色以及风格。
对现有的、也许需要检索的商品系列(例如大小和颜色)而言的处理方法:

  • 在一个单一文档中存储一个商品以及它所有的系列
    这种方法拥有能够在一个单一查询中检索一个商品以及其所有系列的优点。
    然而,它并不是在所有情况下都是最好的方法。避免无限制的文档增长是一个非常重要的最佳实践。
    如果产品系列的数据以及它们相关数据非常小,在商品文档中存储这些数据也许会有意义。
  • 创建一个能够关联到主商品的、单独的系列数据模型 (pefer)

维护了在目录中展示主商品以及当用户请求一个更详细的产品视图时对每个系列的快速查询。
也可以保证商品以及系列文档的一个可预测大小

1
2
3
4
5
6
7
8
9
10
11
12
//definition: db.variantion  //关联到主商品的、单独的系列数据模型
{
“_id”: ”93284847362823”, //variant sku, 商品编号
“itemId”: “30671”, //references the main item
“size”: 6.0,
“color”: “red”

}
//通过它们的商品编号来快速检索到特定的商品系列 ???
db.variation.find({_id:”93284847362823”})
//通过对itemId 属性的查询获得某个特定商品的所有系列
db.variation.find({itemId:”30671”}).sort({_id:1})

不同店铺不同价格

Design: 将每个Price文档的_id设置为商品ID或者商品编号(SKU)的一个级联,
并且将商店ID与价格变量相关联,
这种方法也为处理价格提供很大的灵活性,因为它允许我们在商品或者系列级别对商品进行定价

  • 商品:30671_store23
  • 某个特定规格的商品:93284847362823_store23
    可以查询所有价格或者只是某个特定店铺的价格:
  • 所有价格:db.prices.find({_id:/^30671/})
  • 某个特定店铺的价格:db.prices.find({_id:/^30671_store23/})

    可以添加其他组合,
    例如每个店铺群的价格,然后在单个查询中使用$in操作符获取对于一个商品而言所有可能的价格:

1
2
3
4
5
6
7
8
db.prices.find({
_id:{$in:[
30671_store23”,
30671_sgroup12”,
93284847362823_store23”,
93284847362823_sgroup12”
]
})

浏览和搜索商品

挑战:

* 响应时间:在用户浏览的同时,结果的每个页面应该在毫秒内返回  
* 多个属性:伴随着用户选择不同的方面(例如,品牌、大小、颜色等),新的查询必须能够在多个文档属性中运行  
* 系列级别属性:一些用户选择的属性将会在商品级别进行查询,
  例如品牌,但是其它的查询则有可能运行于系列级别上,例如尺寸。
* 多个系列:每个商品都有可能有成千上万个系列,但是我们只希望每个商品只展示一次,因此,结果必须消除重复项。
* 排序:用户需要能够在多个属性上进行排序,例如价格、尺寸,此外排序操作必须能够高效运行。
* 分页:每个页面只返回少量结果,这就要求确定性排序。

Solution: MongoDB提供一个开源的[连接件项目][3]
它允许MongoDB和Apache Solr以及Elasticsearch同时使用。
然而,对于我们的参考架构,我们想完全在MongoDB中实现一个多方面搜索。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 为了实现这个功能,我们创建了另一个集合,用于存储所谓的摘要文档。  
这些文档包含了我们需要基于多个搜索方面对产品目录中商品进行快速检索的所有信息。
{
“_id”: “30671”,
“title”: “Evening Platform Pumps”,
“department”: “Shoes”,
“Category”: “Women/Shoes/Pumps”,
“price”: 149.95,
“attrs”: [“brand”: “Calvin Klein”, …],
“sattrs”: [“style”: ”Designer”, …],
“vars”: [
{
“sku”: “93284847362823”,
“attrs”: [{“size”: 6.0}, {“color”: “red”}, …],
“sattrs”: [{“width”: 8.0}, {“heelHeight”: 5.0}, …],
}, … //Many more SKUs
]
}

通过使用这个数据模型,我们可以创建以下复合索引:
假设用户将会选择部门来重新定义他们的搜索结果。
对于没有部门的一个产品目录,我们可以非常轻易地从另一个像类别或者类型等比较普遍的方面开始。
然后,我们可以执行需要进行多方面搜索的查询,并且快速将结果返回到页面:

  • 部门+属性+类别+ _id
  • 部门+变量属性+类别+ _id
  • 部门+类别+ _id
  • 部门+价格+ _id
  • 部门+评分+ _id
1
2
3
4
5
6
7
8
//从商品ID获取摘要
db.variation.find({_id:”30671”})
//获取特定商品系列的摘要
db.variation.find({vars.sku:”93284847362823”},{“vars.$”:1})
//通过部门获取所有商品的摘要
db.variation.find({department:”Shoes”})
//使用一系列混合的参数获取摘要
db.variation.find({ “department”:”Shoes”,“vars.attr”: {“color”:”red”},“category”: “^/Shoes/Women”})

[库存优化方法][2]

可以通过电商的店铺及应用访问到的、可靠的、集中的库存系统是提高和丰富用户体验中一个非常庞大的基础部分。

想要得到的一些特性:

  • 可靠地检查产品的实时库存
  • 提供用户在某个指定实体店提货的选项
  • 在某个商品有促销的情况下,判断每日补给的需求

设计原则

电商参考架构中的库存系统应该要做的事情:
需要的是构建一个高性能、可水平扩展的系统,
在一个庞大的、地理分布的区域中的店铺和用户都能够与MongoDB进行实时交互来查看和更新目录。

  • 提供一个库存的360°视图,可以在任何时间被任何客户端访问
  • 能够被任何需要库存数据的系统使用
  • 解决大数据量、以读取为主的工作负载,例如:库存检查
  • 解决大数据量的实时写操作,例如:库存更新
  • 支持批量写入操作以更新系统记录
  • 地理上分离
  • 伴随着库存中店铺数量或者商品数量的增多,保持水平扩展

店铺模式

用户案例的一个基本需求是为每个店铺维护一个关于所有库存的、集中的、实时的视图。
首先需要为店铺集合创建视图,从而将的库存与地理位置相联系起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 每个店铺都使用一个相当直接的文档
{
“_id”:ObjectId(“78s89453d8chw28h428f2423”),
“className”:”catalog.Store”,
“storeId”:”store100”,
“name”:”Bessemer Store”,
“address”:{
“addr1”:”1 Main St.”,
“city”:”Bessemer”,
“state”:”AL”,
“zip”:”12345”,
“country”:”USA”
},
“location”:[-86.95444, 33.40178],

}

创建下列的索引来优化在店铺数据中最经常使用读取类型:

  • {“storeId”:1},{“unique”:true}: 获取某个特定商店的库存
  • {“name”:1}:根据名字获取商店名称
  • {“address.zip”:1}: 获取一个邮编内的所有店铺,例如:店铺定位程序
  • {“location”: 2dsphere}:获取某一个特定地理位置周围的所有商店

库存数据模型

创建一个库存集合来跟踪每一个商品以及它们所有商品系列的真实库存量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//将数据从店铺集合复制到库存集合,最小化对数据库的来回读取数目,同时降低应用级的连接
// db.inventory
{
“_id”:”902372093572409542jbf42r2f2432”,
“storeId”:”store100”, // 知道哪个商店有什么商品是非常必要的
“location”:[-86.95444, 33.40178], // 查询离用户附近的库存
“productId”:”20034”, //
“vars”:[
{“sku”:”sku1”, “quantity”:”5”}, // 在商品级别文档中表示库存
{“sku”:”sku2”, “quantity”:”23”}, // 更大的文档以降低数据冗余度,可以减少在库存集合中需要查询或者更新的文档总数
{“sku”:”sku3”, “quantity”:”2”},

]
}

创建索引

  • {storeId:1}: 得到某一个指定商店库存中的所有商品
  • {productId:1},{storeId:1}: 获取一个指定店铺中某个产品的库存
  • {productId:1},{location:”2dsphere”}:获取在一定距离之内的某个产品的所有库存
1
2
3
4
5
6
7
8
// 可以基于’productID’查询我们的库存了
db.inventory.find({
“storeId”:”store100”,
“productId”:“20034”,
“vars.sku”:”sku11736”
},
{“vars.$”:1}
)

Reference

1
2
3

[快的打车][1]

  • 场景

    1亿多用户,超过300个城市里与300万司机取得了联系
    两类业务:快的出租车 & 快的一号专车
    司机与乘客每天高达6百万次的联系
    管理着将近5亿的订单
    支持着每秒钟数以万计的读写操作
    MongoDB每秒支持着5万个操作(读取和写入的比例约为80:20)
    数据库已经增长到5亿多文档,并且还在持续扩展中

  • 应用

    选择指标:性能(延迟、实时更新的速度),可扩展性,易用性
    LBS : MongoDB的地理空间索引及查询
    可扩展性,历史订单数据的存档:顾客每呼叫一辆出租车,行程的起点及终点、司机的身份以及费用等都会被存储在一条单一的记录中
    Redis 缓存
    MySQL 存储顾客运营及订单数据
    Hadoop 将数据从MongoDB和MySQL中拷贝到Hadoop上进行挖掘及分析
    使用Nagios来监测应用及数据库
    使用Java驱动来运行MongoDB2.6

[盛大大数据量项目中的应用][2]

Best Practices
Replica Set: one for primary, one for secondary, one for increamental back up with a arbiter

[电影票预定系统 design idea][3]

需求

每个场次,每个座位,都只有一个库存
每个订单所预定的座位有锁定状态,在支付前对应的作为不能被再次购买
订单涉及到的座位要不全成功,要不全失败
“全国”级的,数据容量不是太大问题,但性能上要支持水平扩展

描述信息文档结构

  • 影院描述

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    CinemaManager.cinema_detail{
    _id:ObjectId,
    name:String,
    city:String,
    location: [,], // [ 120.13, 30.16 ], coordinate
    comments:String
    }
    db.cinema_detail.ensureIndex({city:1, name:1}){
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 2,
    "numIndexesAfter" : 2,
    "ok" : 1
    }
    db.cinema_detail.ensureIndex({location: "2d"}){
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 3,
    "numIndexesAfter" : 4,
    "ok" : 1
    }
  • 影厅描述

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    CinemaManager.theater_detail{
    _id,
    cinema_id,
    name:String,
    seat:{
    row1:[],
    row2:[],
    row3:[],
    ...
    },
    comments:String
    }
    db.theater_detail.ensureIndex({cinema_id:1}){
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
    }
  • 影片描述

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CinemaManager.movie_detail{
    _id,
    name:String,
    director:String,
    actors:[],
    comments:String
    }
    db.movie_detail.ensureIndex({name:1}){
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
    }

影片放映文档结构

放映信息包含放映时间段,放映影厅,票价。虽然Document结构可以做复杂的嵌套,但原则上期望Document尽量小,
利用数据Shard,性能优化。所以在movie_schedule的设计上每个影片的每场放映独立一个Document表达。

  • 描述
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    CinemaManager.movie_schedule{
    _id,
    cinema_id,
    movie_id,
    theater_id,
    start_time:
    end_time,
    comments:String
    }
    db.movie_schedule.ensureIndex({cinema_id:1, movie_id:1, theater_id:1}){
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "numIndexesAfter" : 2,
    "ok" : 1
    }

交易系统

抽象的来看,售卖系统就是对上诉所有集合的一个整合,外加一套库存字段。
我们认为一场放映就是一个主商品,每个座位可以认为是这个商品的SKU,每个SKU都是1份。
通过Reference关系结合movie_schedule与theater_detail,注意这里引用了

1
2
3
4
5
6
7
8
9
10
11
{
_id: ,
movie_schedule_id:
theater_id: ,
seat: {
row1: [2, 2, 2, 2],
row2: [2, 2, 2],
row3: [2, 2, 2, 2],
row4: [2, 2, 2, 2, 2],
}
}

不仅是Reference的引用关系,还复制了theater_detail.seat字段,每个seat都有一个库存数字,
因为在MongoDB中一个Document的操作是可以保证原子的,不需要对Collection加任何锁。数字2并不是表示可以卖2次:

  • 数字2表示,可销售
  • 数字1表示,已锁定
  • 数字0表示,已售完

From

1
2
3

Humongous, MongoDB = JSON +Index

Features

document/Object/collection oriented, JSON/BSON, Key/Value, fully indexed(object)
Replication & High Availability, Rich Document based Query and Update
auto-sharding(sharding key) & GridFS, Map/Reduce

ReplicaSet

Basically master/slave replication
automatic fail-over to recovery member nodes
consists of two or more nodes that are copies of each other
automatically elects a primary(master) if there is no primary currently available
Distribute read load
arbiter

Monitoring & Diagnostics

  • query profiler
  • Http console
  • mongostat
  • Memory-mapping, keep indexes in memory
  • oplog {ts:{},h{}, op:{},ns:{},o:{},o2:{}}, local, db.oplog.

backup

  • mongoexport, monogoimport
  • mongodump mongorestore
  • data consistency use —oplog
  • use increamental backup
  • read oplog and replay(wordnik tools)

Rollback MongoDB

  • Use snapshot + oplog
  • Use delayed secondary + oplog

Ops

  • Scala up : Vertical
  • Scale out: horizontal / auto sharding
  • build new index in a huge collections

Scenario

  • real time CRUD, web 2.0
  • buffer, persistent buffer
  • big data, web log
  • db cluster

others

no transaction support

Italics and Bold

1
** _
1
2
# ## ### #### ...
#### _italics header five_
1
[visit url](url)
  • inline link
  • reference link [][]
1
2
3
4
5
Find something by your [favorites search engine][favorite];
or try [another][alternative].

[favorite]:www.google.com
[alternative]:www.bing.com

Images

1
![](url "alt text")
  • inline image
  • reference image
    1
    2
    3
    4
    5
    6
       //inline style
    ![text](url "alt text")
    //reference style
    ![text][logo]

    [logo]:url 'alt text'

block quotes >

> this is a block quotes to notify reader pay special attention

Lists *

1
* + -

Paragraphs

  • hard break
  • soft break : two spaces after each new line

Code `

sample 1

1
2
var str ="test";
console.log(str)

sample 2
Inline code test var num = 123 , good sample.
sample 3

1
$npm install express -g

Tablestables is not part of core Markdown spec. by GFM spec?

Horizontal rule

three or more - * _ hyphen Asterisks underscore

inline HTML support

youtube videos sample

sample

1
<iframe width="560" height="315" src="https://www.youtube.com/embed/6A5EpqqDOdk" frameborder="0" allowfullscreen></iframe>

总理提出的互联网+,貌似总理只是顺应当下的互联网趋势而提出,但其实内容是相当丰富的。即:让国家做顶层的设计,大框架的分配社会资源,接着让创业者去填满社会方方面面宏观调控无法渗透和填满的市场空间。大道至简,三个字和一个符号就把民族复兴的基调定好了。

中国传统企业+Internet

互联网

  • 互联网思维
  • 互联网平台/生态
  • 互联网渠道
  • 万物互联 / 物联网

    传统行业: 交易,金融,地产,音乐,娱乐…
    +信息 +交易 +…. //online 上线/线上

=> 营销渠道

+

  • +政策 & +人才
  • +技术
    移动互联 与计算 大数据 物联网
    LBS NFC Bluetooth
    O2O

=> +服务