- 浏览: 253982 次
- 性别:
- 来自: 北京
-
文章分类
最新评论
-
runjia1987:
cpu 100%,是因为读=-1时,没有解注册OP_READ, ...
java nio网络编程的一点心得 -
two_plus:
按照你的例子 服务端变成读循环了...
java nio网络编程的一点心得 -
ilovemyyang:
我也遇到同样的空循环问题,也是百度才找到了的,楼主给力哦
java nio网络编程的一点心得 -
高攀sky:
Python发邮件 -
fobdddf:
fobdddf 写道朋友你好,我试了一下数组的那个例子,反汇编 ...
读《深入理解计算机系统》
Mongodb作为典型的文档数据库,支持内嵌文档和复杂的查询,这给数据库建模带来了更多的灵活性。在一个博客应用中,有博客(Blog)和评论(Comment),每篇博客可以有多条评论。在关系数据库建模中,通常博客和评论分别对应一张表,评论表有到博客表的外键。在MongoDB中,也可以像关系型数据库那样,将博客和评论分别放到不同的集合中,另外也可以选择将评论嵌入到博客文档中。对于后者,一个博客的数据结构可能像这样:
这种方式的好处显而易见,它更加符合对象模型,也可以利用MongoDB的事务,因为MongoDB并不支持跨文档的事务。当然这样做也有坏处,最明显的缺陷在于MongoDB限制单个文档大小最大只能为16M。若每条评论大小以1K计,那么一个文档最多可容纳16K的评论,这个数字实在不小,中国最著名的博主韩寒,去掉被和谐的博客和评论,评论数最多的博客也只有不到6万条评论。而另一个可能的问题是性能,在文档中插入内嵌文档的性能如何,这是我主要关注的问题。
为了考虑文档大小对内嵌文档插入速度的影响,分别考虑每篇博客有1条、10条、100条、1000条、10000条评论的情况。每种情况均插入1万条评论,博客只有1条评论时同时插入1万个博客文档,有10条评论时同时插入1000个博客文档,有100条评论时同时插入100个博客文档,依此类推。另外为了对比内嵌文档插入和独立文档插入的速度,我也需要记录在独立集合中插入1万条评论的时间。MongoDB同时支持异步插入和同步插入,为了排除它的影响,两种情况我都作了测试。
测试代码如下,使用python,每条评论大小为1K。insert_embbded_comments是内嵌文档插入,参数n是每篇博客的评论数目,comment_len是每条评论的长度,这里总为1000,count为插入的博客数目,count*n总为10000,safe表示是否为同步插入,函数返回结果为执行时间。insert_comments和insert_embbded_comments基本一样,只是在独立集合中插入评论。
在我的笔记本(Ubuntu10.04, MongoDB1.8)上运行結果:
前部分是异步插入,后部分是同步插入。标有Embeded的那一行是插入内嵌评论文档的执行时间,第一列,即标有1(x10000)的列,每篇博客有1条评论,共插入1万篇博客。第二列,即标有10(x1000)的列,每篇博客有10条评论,共插入1000篇博客。第三列,每篇博客有100条评论,共插入100篇博客。最后一列,每篇博客有1万条评论,只插入1篇博客。标有Non-embeded的行是是插入独立评论文档的执行结果,都是插入10000条评论,这一行执行时间基本相同。
可以发现,异步插入时,当嵌入的评论数目比较少时(不多于100时),内嵌插入速度与在独立插入速度基本相同,多出来的时间可能是因为插入了额外的博客文档所致。嵌入评论数目为1时的执行时间几乎是嵌入评论数目为10时的两倍,这是因为前者比后者多插入了9000条博客文档。可是当嵌入评论数目达到1000之后,插入速度慢了2.3倍,嵌入评论数目为10000时,内嵌插入比独立插入慢了26.5倍。同步插入时情况基本相同,只是所有速度都慢了将近3倍。
在运行测试程序的同时运行mongostat可以观察到更多MongoDB的运行细节。下面是当嵌入评论数目为10000时执行内嵌插入时,mongostat的输出结果:
下面是执行独立插入时mongostat的输出结果:
首先注意在内嵌插入时锁占用率(locked %栏)达到甚至100%,而独立插入时锁占用率只有大概10%。我注意到,在测试时异步内嵌插入时,当输出10000(x1)列结果后,大概要等1分钟时间才看到输出Non-embeded,这可能是因为Mongodb此时还在持有锁,drop_database操作需要等待插入操作执行完成。另外注意内嵌插入时faults栏一直为0,表示不存在换页,而独立插入时存在换页。在我们的例子中文档大小大约是10M(10000x1K),这么大的文档,不换页并不正常,这说明MongoDB更新文档时总是将整个文档加载到内存,即使只更新部分内容(例如只插入一条评论时),也不能只将部分文档加载到内存,而将其它部分换出到磁盘。从mapped和vsize列可以看出,内嵌插入时要比独立插入时需要更多内存,这是因为独立插入时可以将其它文档暂时换出到磁盘上,降低内存占用,这也是为什么独立插入时faults列不为0的原因。
结论:在MongoDB不宜在文档中内嵌许多子文档。对于像博客这样的应用,将大量评论嵌入到博客文档中会严重影响性能,这是因为MongoDB更新文档时总是将整个文档加载到内存,即使只更新部分内容也是如此。另外,这里并没有考虑随机插入的情况,插入内嵌评论时,如果博客文档原来预留空间不足,将导致原有文档删除,并在数据库文件末尾分配新的文档,这也会劣化内嵌文档的插入性能,还会在数据库中文件中留下大量空洞。
> db.blog.findOne() { _id: 1, title: "No Free Lunch", author: "Alex", comments: [ { who: "John", comment: "I agree" }, { who: "Felix", comment: "You must be joking..." }, ] }
这种方式的好处显而易见,它更加符合对象模型,也可以利用MongoDB的事务,因为MongoDB并不支持跨文档的事务。当然这样做也有坏处,最明显的缺陷在于MongoDB限制单个文档大小最大只能为16M。若每条评论大小以1K计,那么一个文档最多可容纳16K的评论,这个数字实在不小,中国最著名的博主韩寒,去掉被和谐的博客和评论,评论数最多的博客也只有不到6万条评论。而另一个可能的问题是性能,在文档中插入内嵌文档的性能如何,这是我主要关注的问题。
为了考虑文档大小对内嵌文档插入速度的影响,分别考虑每篇博客有1条、10条、100条、1000条、10000条评论的情况。每种情况均插入1万条评论,博客只有1条评论时同时插入1万个博客文档,有10条评论时同时插入1000个博客文档,有100条评论时同时插入100个博客文档,依此类推。另外为了对比内嵌文档插入和独立文档插入的速度,我也需要记录在独立集合中插入1万条评论的时间。MongoDB同时支持异步插入和同步插入,为了排除它的影响,两种情况我都作了测试。
测试代码如下,使用python,每条评论大小为1K。insert_embbded_comments是内嵌文档插入,参数n是每篇博客的评论数目,comment_len是每条评论的长度,这里总为1000,count为插入的博客数目,count*n总为10000,safe表示是否为同步插入,函数返回结果为执行时间。insert_comments和insert_embbded_comments基本一样,只是在独立集合中插入评论。
from pymongo import Connection import sys, time # 目的:测试Mongo中内嵌文档的插入速度 conn = Connection() db = conn.bench conn.drop_database('bench') def insert_embbded_comments(n, comment_len, count=1, safe=False): comment_text = 'a'*comment_len start = time.time() for c in xrange(count): blog = {'_id': c, 'title': 'Mongodb Benchmark'} db.blog.insert(blog) for i in xrange(n): db.blog.update({'_id': c}, {'$push': { 'comments': {'comment': comment_text}}}, safe=safe) end = time.time() return end - start def insert_comments(n, comment_len, count=1, safe=False): comment_text = 'a'*comment_len start = time.time() for c in xrange(count): for i in xrange(n): db.blog.comments.insert({'comment': comment_text}, safe=safe) end = time.time() return end - start def bench(safe=False): total = 10000 print '===== %sINSERT %s comments =====' % ('SAFE ' if safe else '', total) print '%12s %15s %15s %15s %15s %15s' % ('', '1(x10000)', '10(x1000)', '100(x100)', '1000(x10)', '10000(x1)') sys.stdout.write('%12s ' % 'Embeded') sys.stdout.flush() row_types = (1, 10, 100, 1000, 10000) for nrows in row_types: conn.drop_database('bench') count = total / nrows time = insert_embbded_comments(nrows, 1000, count=count, safe=safe) sys.stdout.write('%15s%s' % (time, '\n' if nrows==row_types[-1] else ' ')) sys.stdout.flush() sys.stdout.write('%12s ' % 'Non-embeded') for nrows in row_types: count = total / nrows conn.drop_database('bench') time = insert_comments(nrows, 1000, count=count, safe=safe) sys.stdout.write('%15s%s' % (time, '\n' if nrows==row_types[-1] else ' ')) sys.stdout.flush() bench() bench(safe=True)
在我的笔记本(Ubuntu10.04, MongoDB1.8)上运行結果:
===== INSERT 10000 comments ===== 1(x10000) 10(x1000) 100(x100) 1000(x10) 10000(x1) Embeded 2.31141519547 1.42457890511 1.34223604202 4.3767850399 35.7308151722 Non-embeded 1.29936504364 1.30167293549 1.30044412613 1.29023313522 1.29240202904 ===== SAFE INSERT 10000 comments ===== 1(x10000) 10(x1000) 100(x100) 1000(x10) 10000(x1) Embeded 5.45804405212 4.29802298546 4.95570802689 13.7657668591 107.089906216 Non-embeded 3.68912506104 3.65784692764 3.77990913391 3.66531991959 3.70736408234
前部分是异步插入,后部分是同步插入。标有Embeded的那一行是插入内嵌评论文档的执行时间,第一列,即标有1(x10000)的列,每篇博客有1条评论,共插入1万篇博客。第二列,即标有10(x1000)的列,每篇博客有10条评论,共插入1000篇博客。第三列,每篇博客有100条评论,共插入100篇博客。最后一列,每篇博客有1万条评论,只插入1篇博客。标有Non-embeded的行是是插入独立评论文档的执行结果,都是插入10000条评论,这一行执行时间基本相同。
可以发现,异步插入时,当嵌入的评论数目比较少时(不多于100时),内嵌插入速度与在独立插入速度基本相同,多出来的时间可能是因为插入了额外的博客文档所致。嵌入评论数目为1时的执行时间几乎是嵌入评论数目为10时的两倍,这是因为前者比后者多插入了9000条博客文档。可是当嵌入评论数目达到1000之后,插入速度慢了2.3倍,嵌入评论数目为10000时,内嵌插入比独立插入慢了26.5倍。同步插入时情况基本相同,只是所有速度都慢了将近3倍。
在运行测试程序的同时运行mongostat可以观察到更多MongoDB的运行细节。下面是当嵌入评论数目为10000时执行内嵌插入时,mongostat的输出结果:
insert query update delete getmore command flushes mapped vsize res faults locked % idx miss % qr|qw ar|aw netIn netOut conn time 0 0 11 0 0 12 0 128m 242m 59m 0 86.9 0 0|0 0|1 12k 2k 2 20:36:23 0 0 10 0 0 11 0 128m 242m 56m 0 110 0 0|0 0|1 11k 2k 2 20:36:24 0 0 7 0 0 8 0 128m 242m 59m 0 80.9 0 0|0 0|1 8k 1k 2 20:36:25 0 0 7 0 0 8 0 128m 242m 59m 0 111 0 0|0 0|1 8k 1k 2 20:36:26 0 0 32 0 0 33 0 128m 242m 56m 0 104 0 0|0 0|1 37k 4k 2 20:36:27 0 0 54 0 0 55 1 128m 242m 56m 0 96.8 0 0|0 0|1 62k 6k 2 20:36:28 0 0 54 0 0 55 0 128m 243m 52m 0 97.3 0 0|0 0|1 62k 6k 2 20:36:29 0 0 53 0 0 54 0 128m 243m 60m 0 95.9 0 0|0 0|1 61k 6k 2 20:36:30 0 0 53 0 0 54 0 128m 243m 60m 0 96.9 0 0|0 0|1 61k 6k 2 20:36:31 0 0 53 0 0 54 0 128m 243m 60m 0 97.2 0 0|0 0|1 61k 6k 2 20:36:32
下面是执行独立插入时mongostat的输出结果:
insert query update delete getmore command flushes mapped vsize res faults locked % idx miss % qr|qw ar|aw netIn netOut conn time 2582 0 0 0 0 2584 0 32m 136m 22m 5 10.2 0 0|0 0|0 2m 215k 2 20:36:53 2746 0 0 0 0 2747 0 32m 136m 25m 1 7.5 0 0|0 0|0 3m 229k 2 20:36:54 2728 0 0 0 0 2729 0 32m 136m 28m 4 7.6 0 0|0 0|0 3m 227k 2 20:36:55 2713 0 0 0 0 2714 0 32m 136m 30m 2 7.5 0 0|0 0|0 3m 226k 2 20:36:56 2618 0 0 0 0 2620 0 32m 136m 23m 4 10.2 0 0|0 0|0 2m 218k 2 20:36:57 2756 0 0 0 0 2757 0 32m 136m 26m 2 7.6 0 0|0 0|0 3m 229k 2 20:36:58 2711 0 0 0 0 2712 0 32m 136m 28m 4 7.4 0 0|0 0|0 3m 226k 2 20:36:59 2417 0 0 0 0 2418 0 32m 136m 31m 1 6.6 0 0|0 0|0 2m 201k 1 20:37:00
首先注意在内嵌插入时锁占用率(locked %栏)达到甚至100%,而独立插入时锁占用率只有大概10%。我注意到,在测试时异步内嵌插入时,当输出10000(x1)列结果后,大概要等1分钟时间才看到输出Non-embeded,这可能是因为Mongodb此时还在持有锁,drop_database操作需要等待插入操作执行完成。另外注意内嵌插入时faults栏一直为0,表示不存在换页,而独立插入时存在换页。在我们的例子中文档大小大约是10M(10000x1K),这么大的文档,不换页并不正常,这说明MongoDB更新文档时总是将整个文档加载到内存,即使只更新部分内容(例如只插入一条评论时),也不能只将部分文档加载到内存,而将其它部分换出到磁盘。从mapped和vsize列可以看出,内嵌插入时要比独立插入时需要更多内存,这是因为独立插入时可以将其它文档暂时换出到磁盘上,降低内存占用,这也是为什么独立插入时faults列不为0的原因。
结论:在MongoDB不宜在文档中内嵌许多子文档。对于像博客这样的应用,将大量评论嵌入到博客文档中会严重影响性能,这是因为MongoDB更新文档时总是将整个文档加载到内存,即使只更新部分内容也是如此。另外,这里并没有考虑随机插入的情况,插入内嵌评论时,如果博客文档原来预留空间不足,将导致原有文档删除,并在数据库文件末尾分配新的文档,这也会劣化内嵌文档的插入性能,还会在数据库中文件中留下大量空洞。
评论
2 楼
yang_44
2012-03-11
请教一下,在一些频繁更改状态的数据中,比如一个任务有执行状态,如果使用内嵌文档的方式记录状态值,每个任务也只保留一条状态文档,代表当前状态,是不是比直接update更新文档的键值快呢?
1 楼
violetluna
2012-02-03
非常好!
![](/images/smiles/icon_smile.gif)
![](/images/smiles/icon_smile.gif)
发表评论
-
Python模板性能评测
2011-11-28 18:00 5615本篇主要是对django, jinja ... -
支持通配符的DNS代理
2011-05-05 22:23 7002hosts文件可以手动配置一些域名的IP地址,它的优先级最高, ... -
python变量覆盖陷阱
2011-04-03 21:35 3689我已经好几次碰到这样的错误了,每次碰到都花费我大量的时间,下面 ... -
在App Engine下运行Django 1.2
2011-03-20 22:12 1934我不知道App Engine是从什 ... -
扩充mongodb shell
2011-02-25 23:18 2075因为项目对mongodb数据库的结构做了一点修改,仅仅是是因为 ... -
实现简单的Future
2011-02-19 19:38 1622异步调用的困难之处我们不知道它什么时候会返回,通常的解决方法是 ... -
创建简单声明式python对象模型
2011-02-19 12:27 2744创建简单python声明式对象模型 要创建一个简单的Auth ... -
用RestrictedPython创建Python Sandbox
2011-02-13 17:35 3028众所周知在Python下可以 ... -
wxpython中工作线程与主线程交互
2011-01-15 23:56 6084在wxpython中,像其它GUI ... -
Python下Base64编码
2010-12-04 12:53 2603在Python下有Base64编码的库,可直接用,我自己写了个 ... -
Python中用MetaClass实现委托、不可变集合
2010-09-13 16:01 5341委托(delegate)是许多设计模式(如Decorator, ... -
用编程语言作数据库测试的配置
2010-09-11 11:25 1327在上一篇博客:django, mo ... -
django, mongodb与测试
2010-09-11 10:05 3321在django下很容易写测试,只需要继承DjangoTestC ... -
一个简单的Form向导实现
2010-06-20 18:42 1375Django的contrib中带了个FormWizard,但是 ... -
再一个用于调试Django模板的标签
2010-06-15 17:42 1872主要用于在Html中递归输出对象的属性,这对调试Django相 ... -
几个用于调试Django模板的标签
2010-06-15 17:18 4601Django模板有诸多限制,例如不能调用方法,不能执行任意的P ... -
探索Python下的property, classmethod, staticmethod的实现
2010-05-30 21:10 4116Python有几个内置方法poperty, classmeth ... -
Python农历公历转换
2010-04-24 17:38 2248实现中主要有两个方法,一个是get_lunar_date,它根 ... -
Python发邮件
2010-03-27 11:25 2383下面的代码的作用是将电子书作为附件发送到163邮箱。 ... -
HSQL 索引内部实现
2007-07-07 18:47 3176HSQL使用AVL Tree来实现索 ...
相关推荐
codemirror版本:https://codemirror.net/5/doc/releases.html
外国电影演员识别系统源码分享
mf3010 打印扫描一体机驱动管理软件。
2024免费毕业设计成品,包括源码+数据库+往届论文资料 启动教程:https://www.bilibili.com/video/BV11ktveuE2d 讲解视频:https://www.bilibili.com/video/BV1YfkHYwEME 二次开发教程:https://www.bilibili.com/video/BV1Cw2rY1ErC
chrome-headless-shell-linux64-135.0.7004.0 (Canary).zip
DeepSeek大模型介绍与展望.pptx
英特尔的公版原理图和PCB,cadence版本
《单容水箱液位精准调控:模糊控制策略的深度研究与复现》,单容水箱液位随动系统的模糊控制研究 模糊控制lunwen复现 期刊:化工与自动化仪表(2021年) 图1为结构图,图9为原文结构图, 版本不一样,器件略有调整 图7为结果图,图8为原文结果图 ,单容水箱液位;模糊控制;研究;论文复现;期刊;化工与自动化仪表;结构图;结果图;版本差异;器件调整,"模糊控制研究在单容水箱液位随动系统中的应用与复现"
一个windows上使用的搜索小工具
内容: 这份数据集包含了来自国际大洋发现计划(IODP)第342航次站点U1405、U1406、U1407、U1409和U1410的浮游有孔虫碳酸盐团簇同位素、稳定氧和碳同位素,以及沉积物中的GDGT(甘油二烷基甘油四醚)和烯酮数据。这些站点位于北大西洋的新foundland脊(U1407、U1409和U1410)和J-异常脊(U1405和U1406),用于创建覆盖整个新生代的几乎连续但低分辨率(约每92万年一个样本)的数据拼接,并重建了碳酸盐团簇同位素、TEX86和UK'37海表温度。每个样本包含20立方厘米的沉积物,覆盖2厘米的核心深度区间。年龄模型主要基于详细的船上生物-磁性地层学研究(Norris等,2014)。然而,在40.8 Ma至44.8 Ma时间段内,使用了Cappelli等人(2019)更新的U1410站点年龄模型,通过与U1408站点的年龄模型对比来确定。 访问此数据集,请点击这里:"" ()。
厨房用品分割系统源码&数据集分享
.
监控鞋类物品检测系统源码分享
2024免费毕业设计成品,包括源码+数据库+往届论文资料 启动教程:https://www.bilibili.com/video/BV11ktveuE2d 讲解视频:https://www.bilibili.com/video/BV1YfkHYwEME 二次开发教程:https://www.bilibili.com/video/BV1Cw2rY1ErC
曲线图异常波形检测系统源码分享
内容概要:本文介绍了动车组车号自动识别的现状及其存在的问题,提出了基于图像识别技术的新方法。文中详述了传统人工识别与RFID识别方法的不足,重点阐述了一种新的图像识别系统的设计与实施方案,该系统能够实现在多种恶劣环境下高效精确地获取动车组车号,并通过实际案例展示了这套系统的优势以及其在铁路行业的广阔应用前景。 适用人群:从事铁路运输管理、轨道交通系统开发维护的技术人员,尤其是负责动车组调度、监控及维修工作的相关人员。 使用场景及目标:①用于替代现有人工记录与RFID标签方式,提升动车组车号识别精度与效率;②适用于各种天气状况下的户外作业场景;③旨在构建更加智能化、信息化程度更高的铁路运输体系,助力智慧动车段建设。 其他说明:文中还包括具体的实验对比和技术细节分析,如不同的开机触发装置选择、图像采集设备参数设置、补光措施及识别算法的设计,强调了实际应用场景中可能遇到的问题以及相应的解决方案。
基于AnythingLLM框架和Ollama环境本地运行deepseek,并可以通过用户自己的文档来针对性地回答用户问题,用户也可以上传文件来构建模型回复问题所需要的所有参考资料的知识库,使得模型相对于在线模型更加专业地解答用户的问题。同时本地部署保证了隐私性和针对性。
指针式表盘指针关键部位分割系统源码&数据集分享
多策略增强:MWOA鲸鱼优化算法与其他变体及2024年最新算法的实证比较与结果分析——新颖策略实施效果显著且复杂度无增加的研究,多策略改进的鲸鱼优化算法(MWOA),与其他三种变体和几种2024最新算法比较,策略都是很新颖的策略,可以直接写了发文章,并且没有增加复杂度上改进效果 ,MWOA; 变体算法; 最新算法; 策略新颖; 复杂度未增加; 改进效果显著,"多策略改进MWOA算法:与多种变体及2024新算法比较展示优越性"
织物缺陷检测系统源码分享[一条龙教学YOLOV8标注好的数据集一键训练_70+全套改进创新点发刊_Web前端展示]