使用 MongoDB 存储商品分类信息
此文已由作者温正湖授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
这是一篇MongoDB官网上的一篇文章,分析了使用MongoDB存储商品分类信息相比其他数据库的优势,并讲述了如何将其保存到MongoDB中。原址点击:User case – Product Catalog。MongoDB中文社区有大神已将其翻译成中文。在此不重复造车,直接转载。欢迎一起探讨。
关系型数据库解决方案
上述问题使用传统的关系型数据库也可以解决,比如以下几种方案
针对不同商品,创建不同的表
比如音乐专辑、电影这2种商品,有一部分共同的属性,但也有很多自身特有的属性,可以创建2个不同的表,拥有不同的schema。
CREATE TABLE `product_audio_album` (
`sku` char(8) NOT NULL,
...
`artist` varchar(255) DEFAULT NULL,
`genre_0` varchar(255) DEFAULT NULL,
`genre_1` varchar(255) DEFAULT NULL,
...,
PRIMARY KEY(`sku`))
...
CREATE TABLE `product_film` (
`sku` char(8) NOT NULL,
...
`title` varchar(255) DEFAULT NULL,
`rating` char(8) DEFAULT NULL,
...,
PRIMARY KEY(`sku`))
...
这种做法的主要问题在于
针对每个新的商品分类,都需要创建新的表
应用程序开发者必须显式的将请求分发到对应的表上来查询,一次查询多种商品实现起来比较麻烦
所有商品存储到单张表
CREATE TABLE `product` (
`sku` char(8) NOT NULL,
...
`artist` varchar(255) DEFAULT NULL,
`genre_0` varchar(255) DEFAULT NULL,
`genre_1` varchar(255) DEFAULT NULL,
...
`title` varchar(255) DEFAULT NULL,
`rating` char(8) DEFAULT NULL,
..., PRIMARY KEY(`sku`))
将所有的商品存储到一张表,这张表包含所有商品需要的属性,不同的商品根据需要设置不同的属性,这种方法使得商品查询比较简单,并且允许一个查询跨多种商品,但缺点是浪费的空间比较多。
提取公共属性,多表继承
CREATE TABLE `product` (
`sku` char(8) NOT NULL,
`title` varchar(255) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`price`, ...
PRIMARY KEY(`sku`)) CREATE TABLE `product_audio_album` (
`sku` char(8) NOT NULL,
...
`artist` varchar(255) DEFAULT NULL,
`genre_0` varchar(255) DEFAULT NULL,
`genre_1` varchar(255) DEFAULT NULL,
...,
PRIMARY KEY(`sku`),
FOREIGN KEY(`sku`) REFERENCES `product`(`sku`))
...
CREATE TABLE `product_film` (
`sku` char(8) NOT NULL,
...
`title` varchar(255) DEFAULT NULL,
`rating` char(8) DEFAULT NULL,
...,
PRIMARY KEY(`sku`),
FOREIGN KEY(`sku`) REFERENCES `product`(`sku`))
...
上述方案将所有商品公共的属性提取出来,将公共属性存储到一张表里,每种商品根据自身的需要创建新的表,新表里只存储该商品特有的信息。
Entity Attribute Values 形式存储
所有的数据按照 的3元组的形式存储,这个方案实际上是把关系型数据库当KV存储使用,模型简单,但应对复杂的查询不是很方便。
ENTITY | ATTRIBUTE | VALUES |
---|---|---|
sku_00e8da9b | type | Audio Album |
sku_00e8da9b | title | A Love Supreme |
sku_00e8da9b | … | … |
sku_00e8da9b | artist | John Coltrane |
sku_00e8da9b | genre | Jazz |
sku_00e8da9b | genre | General |
… | … | … |
MongoDB 解决方案
MognoDB 与关系型数据库不同,其无schema,文档内容可以非常灵活的定制,能很好的使用上述商品分类存储的需求; 将商品信息存储在一个集合里,集合里不同的商品可以自定义文档内容。
比如一个音乐专辑可以类似如下的文档结构
{
sku: "00e8da9b",
type: "Audio Album",
title: "A Love Supreme",
description: "by John Coltrane",
asin: "B0000A118M", shipping: {
weight: 6,
dimensions: {
width: 10,
height: 10,
depth: 1
},
}, pricing: {
list: 1200,
retail: 1100,
savings: 100,
pct_savings: 8
}, details: {
title: "A Love Supreme [Original Recording Reissued]",
artist: "John Coltrane",
genre: [ "Jazz", "General" ],
...
tracks: [
"A Love Supreme Part I: Acknowledgement",
"A Love Supreme Part II - Resolution",
"A Love Supreme, Part III: Pursuance",
"A Love Supreme, Part IV-Psalm"
],
},
}
而一部电影则可以存储为
{
sku: "00e8da9d",
type: "Film",
...,
asin: "B000P0J0AQ", shipping: { ... }, pricing: { ... }, details: {
title: "The Matrix",
director: [ "Andy Wachowski", "Larry Wachowski" ],
writer: [ "Andy Wachowski", "Larry Wachowski" ],
...,
aspect_ratio: "1.66:1"
},
}
所有商品都拥有一些共同的基本信息,特定的商品可以根据需要扩展独有的内容,非常方便; 基于上述模型,MongoDB 也能很好的服务各类查询。
查询某个演员参演的所有电影,并按发型日志排序
db.products.find({'type': 'Film', 'details.actor': 'Keanu Reeves'}).sort({'details.issue_date', -1})
上述查询也可以通过建立索引来加速
db.products.createIndex({ type: 1, 'details.actor': 1, 'details.issue_date': -1 })
查询标题里包含特定信息的所有电影
db.products.find({
'type': 'Film',
'title': {'$regex': '.*hacker.*', '$options':'i'}}).sort({'details.issue_date', -1})
可建立如下索引来加速查询
db.products.createIndex({ type: 1, details.issue_date: -1, title: 1 })
扩展
当单个节点无法满足海量商品信息存储的需求时,就需要使用MongoDB sharding来扩展,假定大量的查询都是都会基于商品类型,那么就可以使用商品类型字段来进行分片。
db.shardCollection('products', { key: {type: 1} })
分片时,尽量使用复合的索引字段,这样能满足更多的查询需求,比如基于商品类型之后,还会经常根据商品的风格标签来查询,则可以把商品的标签字段作为第二分片key。
db.shardCollection('products', { key: {type: 1, 'details.genre': 1} })
如果某种类型的商品,拥有相同标签的特别多,则会出现jumbo chunk的问题,导致无法迁移,可以进一步的优化分片key,以避免这种情况。
db.shardCollection('products', { key: {type: 1, 'details.genre': 1, sku: 1} })
加入第3分片key之后,即使类型、风格标签都相同,但其sku信息肯定不同,就肯定不会出现超大的chunk。
网易云MongoDB 服务为开发者提供了一站式的 MongoDB 云端解决方案,包括提供三节点复制集的高可用架构,故障切换,并提供专业的备份、监控以及性能优化方案,彻底免除开发者的运维烦恼。点击可免费试用。
网易云免费体验馆,0成本体验20+款云产品!
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 代码混淆防止APP被反编译指南
【推荐】 简单概括一下《金字塔原理》的主要内容?
【推荐】 分布式存储系统可靠性系列二:系统估算示例
使用 MongoDB 存储商品分类信息的更多相关文章
- Mongodb 存储日志信息
线上运行的服务会产生大量的运行及访问日志,日志里会包含一些错误.警告.及用户行为等信息,通常服务会以文本的形式记录日志信息,这样可读性强,方便于日常定位问题,但当产生大量的日志之后,要想从大量日志里挖 ...
- MongoDB 存储引擎和数据模型设计
标签: MongoDB NoSQL MongoDB 存储引擎和数据模型设计 1. 存储引擎 1.1 存储引擎是什么 1.2 MongoDB中的默认存储引擎 2. 数据模型设计 2.1 内嵌和引用 2. ...
- Asp.Net Core Web Api图片上传(一)集成MongoDB存储实例教程
Asp.Net Core Web Api图片上传及MongoDB存储实例教程(一) 图片或者文件上传相信大家在开发中应该都会用到吧,有的时候还要对图片生成缩略图.那么如何在Asp.Net Core W ...
- 使用 MongoDB 存储日志数据
使用 MongoDB 存储日志数据 线上运行的服务会产生大量的运行及访问日志,日志里会包含一些错误.警告.及用户行为等信息.通常服务会以文本的形式记录日志信息,这样可读性强,方便于日常定位问题 ...
- MongoDB存储引擎选择
MongoDB存储引擎选择 MongoDB存储引擎构架 插件式存储引擎, MongoDB 3.0引入了插件式存储引擎API,为第三方的存储引擎厂商加入MongoDB提供了方便,这一变化无疑参考了MyS ...
- MongoDB 存储日志数据
MongoDB 存储日志数据 https://www.cnblogs.com/nongchaoer/archive/2017/01/11/6274242.html 线上运行的服务会产生大量的运行及访问 ...
- MongoDB学习笔记(五、MongoDB存储引擎与索引)
目录: mongoDB存储引擎 mongoDB索引 索引的属性 MongoDB查询优化 mongoDB存储引擎: 目前mongoDB的存储引擎分为三种: 1.WiredTiger存储引擎: a.Con ...
- MongoDB 存储引擎选择
MongoDB存储引擎选择 MongoDB存储引擎构架 插件式存储引擎, MongoDB 3.0引入了插件式存储引擎API,为第三方的存储引擎厂商加入MongoDB提供了方便,这一变化无疑参考了MyS ...
- SharedPreferences漏洞, 无法避免,所以不要在里面存储敏感信息
1. SharedPreferences漏洞, 无法避免,所以不要在里面存储敏感信息2. 数据存储检测,content://com.starcor.launcherInfo/deviceInfo&q ...
随机推荐
- BZOJ1696: [Usaco2007 Feb]Building A New Barn新牛舍
n<=10000个点(xi,yi),找到一个不同于给出的所有点的点,使得该点到所有点的曼哈顿距离最小并找出这样的点的个数. 第一眼看上去这不是中位数嘛,奇数一个点偶数一片,然后找一下这篇区域有几 ...
- Android广播Broadcast
Android Broadcast简单认识 Broadcast是应用程序间传输信息的一种机制,BroadcastReceiver是对发送出来的广播(Broadcast)进行过滤并接收相应的一类组件. ...
- pip命令自动补全功能;设置代理;使用国内源
这是pip自带的功能 执行的脚本 把脚本写入.zshrc或者profile等里面,执行source立即生效 设置代理: pip --proxy=http://username:password@pro ...
- 【APUE】fork函数
#include <unisth.h> pid_t fork(void) fork函数被调用一次,返回两次.子进程的返回值是0,父进程的返回值是子进程的进程id. fork函数调用一次却返 ...
- Semaphore使用
Semaphore使用
- 实例 tar备份以日期命名
tar备份以日期命名****************************************************************************************#v ...
- 万恶之源:C语言中的隐式函数声明
1 什么是C语言的隐式函数声明 在C语言中,函数在调用前不一定非要声明.如果没有声明,那么编译器会自己主动依照一种隐式声明的规则,为调用函数的C代码产生汇编代码.以下是一个样例: int main(i ...
- 【HDOJ 3652】B-number
[HDOJ 3652]B-number 给一整数n 找<=n的整数中能被13整除且含有13的 数位dp 记忆化! . 一入记忆化深似海. ..再也不想用递推了...发现真的非常好想 仅仅要保证满 ...
- Java面试通关要点汇总整理【终极版】(转载)
简历篇 请自我介绍 请介绍项目 基础篇 基本功 面向对象的特征 final, finally, finalize 的区别 int 和 Integer 有什么区别 重载和重写的区别 抽象类和接口有什么区 ...
- dm385的分辨率切换
建议用两个RSZ的输出来完成切换分辨率功能,帧率可以通过软件丢帧来实现. 两个SWMS增加了两个1080p60的读和写,对系统影响是比较大的. http://www.deyisupport.com/q ...