MongoDB 自增 id 的生成

2018-10-18 16:21:35   最后更新: 2018-10-18 16:24:12   访问数量:129




之前我们使用 MySQL 等关系型数据库时,主键都是设置成自增的

但在分布式环境下,尤其是在分库分表以后,单纯的自增主键会产生冲突,需要考虑如何生成唯一 ID

这一点上,mongodb 预先考虑到并采取措施保证了分布式环境中生成的 id 的唯一性

那么,mongodb 是如何做的呢?这么做有什么好处,又有什么不足呢?本文我们就来介绍一下

 

mongodb 采用了一个称之为 ObjectId 的类型来做主键,ObjectId 是一个12字节的 BSON 类型字符串,如下图所示

 

 

前9个字节就保证了同一秒钟不同机器不同进程产生的 ObjectId 是唯一的

而最后三个字节则在每一个进程中通过生成随机数,并以此为基础自增,确保相同进程的同一秒产生的ID也是不同的

每个进程一秒钟可以最多拥有 256^3(16777216)个不同的 ObjectId 而不会产生冲突

 

与此同时,在 _id 中已经保存了时间信息,让我们可以轻易的获取到文档首次插入的时间:

> objid = new ObjectId() > ObjectId("53102b43bf1044ed8b0ba36b") > objid.getTimestamp() > ISODate("2014-02-28T06:22:59Z")

 

 

而另一方面,由于时间戳信息被保存在前 3 个字节中,这使得默认排序下,文档数据可以很容易按照插入顺序排序

 

这样设计的主键 ID 从根本上保证了其唯一性,也因此可以不必由 MongoDB 服务器生成,通常,主键 _id 的生成都是由客户端的驱动程序完成的

这个做法很好的体现了MongoDB的哲学:能交给客户端驱动程序来做的事情就不要交给服务器来做

我们知道,扩展数据服务应用层也要比扩展数据库层容易得多,这样的设计和实现,很大程度上减轻了数据库扩展的负担

 

虽然在同一个进程内的一秒内生成的多个主键 id 是自增的,但是在数据库全局是没有这样的规律的

有时,能够完全自增的 id 对于应用业务来说是非常重要的

同时,'53102b43bf1044ed8b0ba36b' 这样的字符串对于我们来说也非常不直观,更不用说读写和记忆了

MongoDB 允许我们自己生成 _id,但是这样唯一性的压力就又来了,在并发环境下保证自增 ID 的严格自增与避免 ID 冲突有时是需要丰富的经验的

 

虽然已经有很多生成自增 id 的方案可供选用,如依赖 redis 等,但 MongoDB 本身提供了原子操作,我们可以通过 MongoDB 提供的原子操作来实现 id 的自增

MongoDB 的 findAndModify 命令可以指定将获取某个键并同时进行某个操作,比如增加操作,从而实现某个字段的自增

当然,findAndModify 命令是非常强大的,他提供了原子的增删改查操作,本文仅对其自增的方法进行讲解,其他操作较为类似

 

创建 collection

我们先创建一个自动增长 id 的集合:

> db.ids.save({name:"user", id:0}); > db.ids.find(); { "_id" : ObjectId("4c637dbd900f00000000686c"), "name" : "user", "id" : 0 }

 

 

获取自增 id

通过下面的命令就可以获取自增 ID 了

> userid = db.ids.findAndModify({update:{$inc:{'id':1}}, query:{"name":"user"}, new:true}); { "_id" : ObjectId("4c637dbd900f00000000686c"), "name" : "user", "id" : 1 }

 

 

<?php function mid($name, $db){ $update = array('$inc'=>array("id"=>1)); $query = array('name'=>$name); $command = array( 'findandmodify'=>'ids', 'update'=>$update, 'query'=>$query, 'new'=>true, 'upsert'=>true ); $id = $db->command($command); return $id['value']['id']; } $conn = new Mongo(); $db = $conn->idtest; $id = mid('user', $db); $db->user->save(array('uid'=>$id, 'username'=>'techlog', 'password'=>'techlog', 'info'=>'http://techlog.cn')); $conn->close(); ?>

 

 

import pymongo client = pymongo.MongoClient() db = client.techlogdb if db.ids.find_one({'name' : 'user'}) is None: db.ids.save({'name' : 'user', 'id' : 0}) result = db.ids.find_and_modify( query={'name': 'user'}, fields={'id': 1, '_id': 0}, update={'$inc': {'id' : 1}}, new=True ) increment_id = result['id']

 

 

https://www.tutorialspoint.com/mongodb/mongodb_autoincrement_sequence.htm

https://stackoverflow.com/questions/19524725/how-to-autoincrement-id-at-every-insert-in-pymongo

 






技术帖      database      技术分享      数据库      db      分布式      mongodb      非关系型数据库     


京ICP备15018585号