解决方法其实很简单:unique index。 上文提到,name属性是唯一的。如果我们给它加一个唯一索引,不就可以从根本上避免一个人有多个不同的文档了吗?
这个时候,即使两个进程经过搜索都得到了某个文档不存在的结果,假设A先一步创建了该文档,那么当B创建文档时,由于含有相同name的文档已经被A进程抢先创建,MongoDB就会拒绝B进程创建。pymongo对此类错误应该是有应对机制的,这是B进程会稍等片刻,重新尝试更新文档。这个时候,A进程已经完成计票并且释放了写入锁,文档被成功创建,而进程B再尝试时,也会检索到这个被新创建的文档,直接在上面把票数加1,而不是创建新文档。这样一个小的时间差,就解决了写入矛盾。
同时,我们还得到了额外的奖励:当name上创建了unique index之后,找到特定候选人的速度就会快很多。这个优势在计票初期,候选人数量不多时并没有显示,但当后期候选人数变多时,一方面再有新的候选人被加入的概率会变得很小(该被加的差不多都被加进来了),因此修改索引的几率越来越少;另一方面,在候选人基数变得很大的时候,相比于没有索引的情况,有唯一索引的情况下程序的速度优势会越发明显。这两个方面综合在一起,结果就是,添加唯一索引之后程序在后期速度优势会越来越明显。在我自己的程序中,运行初期多线程比单线程只快了三四倍,但在数据量较大时,多线程(加上唯一索引)会比单线程快10到20倍。这多处来的速度,就是唯一索引导致的。
虽然这样能解决高并发产生的重复插入问题,但是我们发现db进行upsert的速度越来越慢,以前两个小时就能消费完队列里的数据,现在需要四五个小时,并且消耗时间是呈现不断上升的趋势。所以我觉得应该是和upsert这个操作有关。
由于是写多读少的场景,所以我们并没有对集合加入索引。并且经查阅资料发现,mongodb索引的存储机制和mysql不同,mysql的索引是存储在硬盘中,需要时会调用部分到内存中。而mongodb的索引则是直接存储在内存和临时文件中,并且和内存大小限制有很直接的关系,如果超过内存限制,则从硬盘加载索引。所以mongodb索引的使用,在大数据集合面前,会面临内存耗尽的风险。
下面这个链接是官方对索引使用限制的说明:
https://docs.mongodb.com/manual/reference/limits/#index-limitations
另外upsert操作会先在集合中进行数据查找,如果数据已经存在,则更新,否则才插入。数据的查找那就势必会使用索引,mongo索引用的是B树,时间复杂度为Olog(n),而没有索引的情况下则时间复杂度是O(n),差别见下图:
问题显而易见了,随着数据日益增多,upsert性能是线性下滑的,所以后来的想法就是,如果有高并发的情况下,还是先进行对数据库查询然后在进行insert,这个时候对这些操作进行加锁操作,当然会对速度很有影响,但是至少规避了后期内存耗尽风险。如果是大公司有钱那就用upsert+唯一索引来解决吧,内存不够就扩充内存,钞能力可以解决任何问题。
参考文档:https://blog.csdn.net/jeffrey11223/article/details/80366368
关于mongoDB插入去重以及高并发问题最近在项目中碰到过向mongoDB插入数据去重问题。一开始我的想法直接用upsert,我的项目部分代码如下: //使用Upsert进行插入,如果存在就更新,不存在则插入 //根据报告时间和code进行筛选去重 Query query = new Query(); query.addCriteria(Criteria.where("reporttimeStamp").is(meteorologicalDa
BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。
插入文档
MongoDB 使用 insert() 或 save() 方法向集合中插入文档,语法如下:
db.COLLECTION_NAME.insert(document)
以下文档可以存储在 MongoDB 的 runoob 数据库 的 col 集合中:
>db.col.insert({title: 'Mo
MongoDB的并发
线上环境遇到MongoDB的性能瓶颈,为了解决性能瓶颈学习了一下MongoDB中的并发机制,记录如下。下文中主要是对比了MongoDB 2.2和3.0.7这两个版本的并发机制。
1. MongoDB锁的类型 在2.2版本中MongoDB用的是读写锁,允许并行的读但是只能互斥的写,当一个读锁存在的时候可以有多个读操作共享这个锁,但是当一个写锁存在的时候只能有一个写操作获得这个
MongoDB允许多个客户端读取和写入相同的数据。 为了确保一致性,它使用lock和其他并发控制措施来防止多个客户端同时修改同一条数据。 总之,这些机制保证对单个文档同时只可能被一个客户端写入,并且客户端永远不会看到数据的不一致视图。
MongoDB使用什么类型的锁?
MongoDB使用多粒度锁,允许操作锁定全局,数据库或集合级别,并允许各个存储引擎在集合级别下实现自己的并发控制(例如,在W...
MongoDB是一个文档数据库(以JSON为数据模型),由C++语言编写。MongoDB的数据是存储在硬盘上的,只不过需要操作的数据会被加载到内存中提高效率,所以MongoDB本身很吃内存。(本文章使用4.x版本,自带分布式事务)
MongoDB基于灵活的JSON文档模型,非常适合敏捷式快速开发。与此同时,其与生俱来的高可用、高水平扩展能力使它在处理海量、高并发数据应用时颇具优势。如何考虑是否选择MongoDB?
没有某个业务场景必须要使用MongoDB才能解决,但使用MongoDB通常能让你以更低的成本
以下摘自pymongo文档:
update_one(filter,update,upsert=False)
update_many(filter,update,upsert=False)
filter: A query that matches the document to update.
update: The modifications to apply.
高并发操作MongoDB性能优化
一,代码层级优化
采用批量数据方式操作MongoDB。将数据信息存放在消息队列中进行缓存,然后定时定量的去获取队列消息,触发连接MongoDB获取查询结果。
所谓定时定量是指每一段时间或消息队列达到某一数值。示例:每10秒或队列消息数达到200时提交一次统计事件。
优点:减少网络传输的IO,同时减少SQL语句解析的次数。降低MongoDB日志刷盘的数据量和频率...
方法一:
去重
blackListIp.setIp = "192.168.0.1";
blackListIpRepository.save(blackListIp.getIp);//存入一条数据库中已经有ip=192.168.0.1的记录。现在这个ip的记录有两条。
blackListIpRepository.delete
在 MongoDB 中,可以使用 `distinct()` 方法来进行去重。这个方法可以应用在一个集合中的某个字段,返回不同的值。例如,假设我们有一个集合叫做 `users`,其中有一个字段叫做 `username`,我们可以使用如下的命令来获取所有不同的用户名:
db.users.distinct("username")
这将返回一个包含所有不同用户名的数组。需要注意的是,`distinct()` 方法只能用于一个字段,如果你想要对多个字段进行去重,需要使用聚合框架中的 `$group` 操作符。