请问mongodb时间比较里面 timestamp如何比较呢

MongoDB权威指南——复制 - 看引擎 KENGINE | 看看新闻网 IT资讯
MongoDB权威指南——复制
MongoDB权威指南
第九章 复制
MongoDB的复制功能很重要,尤其是现在的存储引擎还不支持单击持久性。不仅可以用复制来应对故障切换,数据集成,还可以做读扩展,热备份或作为离线批处理的数据源。
9.1 主从复制
主从复制是MongoDB最常用的复制方式。可用于备份,故障恢复和读扩展等。
基本就是搭建一个主节点和一个或多个从节点,每个从节点需要知道主节点的地址。运行mongod --master启动主服务器。运行mongod --slave --source master_address启动从服务器。
[root@test02 ~]# mongod --fork --dbpath /data/node2 --logpath /data/mongodb.log --port 10001 --logappend --master
从节点选择不同的目录和端口,并且用--source为从节点指明主节点的地址。
[root@test02 ~]# mongod --fork --dbpath /data/node3 --logpath /data/mongodb2.log --port 10002 --logappend --slave --source localhost:10001
所有的从节点都是从主节点复制信息,目前还不能从节点到从节点复制机制,原因是从节点没有自己的oplog。
一个集群中从节点没有明确的限制,但是多个节点对单点主机发起的查询也是吃不消的,不超过12个节点的集群可以良好运转。
9.1.1 选项
在从节点上指定只复制特定某个数据库(默认复制所有数据库)。
--slavedelay
用在从节点上,当应用主节点的操作时增加延迟。这样可以轻松设置延时从节点了,这样的节点对于用户无意间删除重要数据或插入垃圾数据起到防护作用。通过延缓操作,可以有个恢复时间差。
--fastsync
以主节点的数据快照为基础启动从节点。如果数据目录一开始时主节点的数据快照,从节点用这个选项启动要比做完整同步块很多。
--autoresync
如果主节点和从节点不同步,可以自动同步了。
--oplogsuze
主节点oplog的大小(单位是MB)。
9.1.2 添加以及删除源
cat && /etc/hosts &&EOF
192.168.27.212 test02
192.168.27.213 test03
192.168.27.214 test01
启动从节点时可以用--source指定主节点,也可以在shell中配置这个源。
[root@test02 ~]# mongod --fork --dbpath /data/node3 --logpath /data/mongodb.log --port 10003 --logappend --slave
将192.168.27.212:10001作为源插入到从节点上。
& db.sources.insert({ "host" : "192.168.27.212:10001"});
立即查询会得到插入的文档:
& use local
switched to db local
& db.sources.find();
{ "_id" : ObjectId("530be9cfe66b7"), "host" : "test02:10001"
当同步完成后,文档更新:
& db.sources.find();
{ "_id" : ObjectId("530bf0ab74c79c"), "host" : "test02:10001", "source" : "main", "syncedTo" : Timestamp(, 1), "dbsNextPass" : { "foo" : true, "test" : true } }
9.2 副本集
副本集就是有自动故障恢复功能的主从集群。主从集群和副本集最为明显的区别就是副本集没有固定的主节点:整个集群会选举出一个主节点,当其不能工作时,则变更到其它节点。副本集总会有一个活跃节点和一个或多个备份节点。
副本集最好的优点就是全自动化的。
mongod --fork --dbpath /data/node2 --logpath /data/mongodb.log --port 10001 --logappend --replSet myrepl/test03:10002
mongod --fork --dbpath /data/node3 --logpath /data/mongodb.log --port 10002 --logappend --replSet myrepl/test02:10001
副本集的亮点是自检测功能:在其中指定单台服务器后,MongoDB会自动搜索并连接其余的节点。
启动几台服务器后,日志会告诉你副本集没有初始化。需要在shell中初始化副本集。
连接任意一个服务器。初始化命令只执行一次:
& db.runCommand({"replSetInitiate" : {
... "_id" : "myrepl",
... "members" : [
"_id" : 1,
"host" : "test02:10001"
"_id" : 2,
"host" : "test03:10002"
"startupStatus" : 4,
"info" : "myrepl/test03:10002",
"errmsg" : "all members and seeds must be reachable to initiate set"
"_id" : "myrepl"
副本集的名称
"members" : [...]
副本集中的服务器列表,每个服务器至少两个键。
每个服务器唯一的ID
"host" : hostname
这个键指定服务器主机
config = {"_id" : "myrepl",
"members" : [
{"_id" : 0, "host" : "test02:10001"},
{"_id" : 1, "host" : "test03:10002"}
rs.initiate(config);
rs.status();
myrepl:SECONDARY& rs.status();
"set" : "myrepl",
"date" : ISODate("T02:17:39Z"),
"myState" : 2,
"syncingTo" : "test03:10002",
"members" : [
"_id" : 0,
"name" : "test02:10001",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 968,
"optime" : Timestamp(, 1),
"optimeDate" : ISODate("T02:14:17Z"),
"errmsg" : "syncing to: test03:10002",
"self" : true
"_id" : 1,
"name" : "test03:10002",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 48,
"optime" : Timestamp(, 1),
"optimeDate" : ISODate("T02:14:17Z"),
"lastHeartbeat" : ISODate("T02:17:38Z"),
"lastHeartbeatRecv" : ISODate("T02:17:39Z"),
"pingMs" : 1,
"syncingTo" : "test02:10001"
如果这时候把primary节点停掉,在secondary节点执行写操作,就会发生如下错误提示:
myrepl:SECONDARY& db.test.insert({name : "baobao"});
not master
如果只有2台Mongodb,配置复制集群还不够安全,需要1个外在角色调整各个节点的角色。
standard:常规节点,存储一份完整的数据副本,参与选举投票,可能称为活跃节点。
passive:存储完整的数据副本,参与投票,不能成为活跃节点。
arbiter:仲裁者只负责投票,不接受复制数据,也不能成为活跃节点。
当Primary宕掉后,可以通过Arbiter在Secodarys中选举一个Primary节点,避免单点故障。
可以增加一个仲裁节点,只负责仲裁,不做数据存储。
mongod --fork --dbpath /data/node1 --logpath /data/mongodb.log --port 10003 --logappend --replSet myrepl/test02:10001,test03:10002
myrepl:PRIMARY& rs.addArb("test01:10003");
{ "ok" : 1 }
查看各节点的状态:
myrepl:PRIMARY& rs.status();
"set" : "myrepl",
"date" : ISODate("T02:30:26Z"),
"myState" : 1,
"members" : [
"_id" : 0,
"name" : "test02:10001",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 1735,
"optime" : Timestamp(, 1),
"optimeDate" : ISODate("T02:30:09Z"),
"self" : true
"_id" : 1,
"name" : "test03:10002",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 204,
"optime" : Timestamp(, 1),
"optimeDate" : ISODate("T02:30:09Z"),
"lastHeartbeat" : ISODate("T02:30:26Z"),
"lastHeartbeatRecv" : ISODate("T02:30:24Z"),
"pingMs" : 1,
"syncingTo" : "test02:10001"
"_id" : 2,
"name" : "test01:10003",
"health" : 1,
"state" : 6,
"stateStr" : "UNKNOWN",
"uptime" : 17,
"lastHeartbeat" : ISODate("T02:30:25Z"),
"lastHeartbeatRecv" : ISODate("T00:00:00Z"),
"pingMs" : 1,
"lastHeartbeatMessage" : "still initializing"
对比三个节点对自身节点性质的判断:
myrepl:PRIMARY& db.isMaster();
"setName" : "myrepl",
"ismaster" : true,
"secondary" : false,
"hosts" : [
"test03:10002",
"test02:10001"
"arbiters" : [
"test01:10003"
"primary" : "test03:10002",
"me" : "test03:10002",
"maxBsonObjectSize" : ,
"maxMessageSizeBytes" : ,
"localTime" : ISODate("T02:32:29.760Z"),
myrepl:SECONDARY& db.isMaster();
"setName" : "myrepl",
"ismaster" : false,
"secondary" : true,
"hosts" : [
"test02:10001",
"test03:10002"
"arbiters" : [
"test01:10003"
"primary" : "test03:10002",
"me" : "test02:10001",
"maxBsonObjectSize" : ,
"maxMessageSizeBytes" : ,
"localTime" : ISODate("T02:33:50.144Z"),
myrepl:SECONDARY& db.isMaster();
"setName" : "myrepl",
"ismaster" : false,
"secondary" : true,
"hosts" : [
"test02:10001",
"test03:10002"
"arbiters" : [
"test01:10003"
"primary" : "test03:10002",
"me" : "test02:10001",
"maxBsonObjectSize" : ,
"maxMessageSizeBytes" : ,
"localTime" : ISODate("T02:33:50.144Z"),
在节点配置中修改priority键,来配置成标准节点或者被动节点。
默认优先级为1,可以是0~1000.
"arbiterOnly"键可以指定仲裁节点。
备份节点会从活跃节点抽取oplog,并执行操作,就像活跃备份系统中的备份服务器一样。活跃节点也会写操作到自己的本地oplog,这样就能成为活跃节点了。oplog中的操作也包括严格递增的序号。通过序号判断数据的时效性。
9.2.3 故障切换和活跃节点的选举
如果活跃节点坏了,其他节点会选一个新的活跃节点。新的活跃节点由副本集中的大多数选举出来。仲裁节点只负责投票,避免出现僵局。新的节点是优先级最高的节点。
活跃节点使用心跳来跟踪集群中多少节点对其可见,如果不超过半数,则活跃节点自动降为备份节点。可以防止活跃节点一直不放权。
无论活跃节点何时变化,新活跃节点的数据被假定为系统的最新数据。其他节点的操作都会回滚,所有节点连接新的活跃节点后要重新同步。这些节点会查看自己的oplog,找出其中活跃节点没有执行过的操作,然后向活跃节点请求这些操作影响的文档的最新副本。
正在执行重新同步的节点被视为恢复中,在完成这个过程前,不能成为活跃节点候选者。
9.3 在从服务器上执行操作
从节点的主要作用是作为故障恢复机制,以防止主节点数据丢失或者停止服务。
可以在从节点做备份的数据源。也可以用来扩展读取性能,或者进行数据处理。
9.3.1 读扩展
用MongoDB扩展读取的一种方式就是将查询放在从节点上,减轻主节点的负载。当负载是读密集型时这样非常不错。当是写密集型时,需要用自动分片来扩展。
使用从节点来扩展MongoDB的读取有个要点,就是数据复制并不同步,就是在主节点插入或更新数据口,有片刻从节点的数据不是最新的。
扩展读取需要打开一个特殊选项slaveOkey,告诉从服务器是否可以处理请求。
如果直接在secondary上操作,会发生如下错误:
myrepl:SECONDARY& db.test.find();
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }
需要告知Mongodb集群,从哪台机器上进行读操作:
myrepl:SECONDARY& rs.slaveOk();
myrepl:SECONDARY& db.test.find();
{ "_id" : ObjectId("530bfc79eee2c2ce39f9cd95"), "name" : "caoqing" }
{ "_id" : ObjectId("530bfd8fdcb32"), "name" : "xiaobao" }
9.3.2 用从节点做数据处理
从节点的另外一个服务就是作为一种机制来减轻密集型处理的负载,或作为聚合,避免影响主节点的性能。用--master启动一个普通的从节点,同时使用--master和--slave矛盾。这意味着如果能对从节点进行写入,像平常一样查询,就把它作为一个主节点。从节点还是会不断的从主节点复制数据。这样就可以对从节点执行阻塞操作而不影响主节点的性能。
从节点第一次启动时不能有正在复制的数据库,如果有,数据库就不能完成同步,只能更新。
用这种技术要保证不能对正在复制主节点数据的从节点上的数据库执行写入。从节点不能恢复这些操作,就不能正确的映射主节点。
9.4 工作原理
MongoDB的复制至少需要两台服务器或者节点,其中一个主节点,负责处理客户端请求,其他的都是从节点,负责映射主节点的数据。主节点记录在其上的所有操作。
从节点定期轮询主节点获取这些操作,然后对数据副本执行这些操作。由于和主节点执行了相同的操作,从节点就能保持和主节点的数据同步。
9.4.1 oplog
主节点的操作记录成为polog(operation log)。oplog存储在一个特殊的数据库里,成为local。oplog就在其中的oplog.$main集合里面。oplog的每个文档都代表主节点执行的一个操作。
myrepl:PRIMARY& db.oplog.$main.help();
查看oplog的内容:
myrepl:PRIMARY&
switched to db local
myrepl:PRIMARY&
replset.minvalid
startup_log
system.indexes
system.replset
myrepl:PRIMARY& db.oplog.rs.find();
{ "ts" : Timestamp(, 1), "h" : NumberLong(0), "v" : 2, "op" : "n", "ns" : "", "o" : { "msg" : "initiating set" } }
{ "ts" : Timestamp(, 1), "h" : NumberLong("-4979055"), "v" : 2, "op" : "i", "ns" : "test.test", "o" : { "_id" : ObjectId("530bfc79eee2c2ce39f9cd95"), "name" : "caoqing" } }
{ "ts" : Timestamp(, 1), "h" : NumberLong("403253"), "v" : 2, "op" : "i", "ns" : "test.test", "o" : { "_id" : ObjectId("530bfd8fdcb32"), "name" : "xiaobao" } }
{ "ts" : Timestamp(, 1), "h" : NumberLong("9102944"), "v" : 2, "op" : "n", "ns" : "", "o" : { "msg" : "Reconfig set", "version" : 2 } }
myrepl:PRIMARY&
文档包含的键如下:
ts 操作的时间戳。时间戳是一种内部类型,用于跟踪操作执行的时间。有4字节的时间戳和4字节的递增计数器构成。
op 操作类型,只有1字节代码。
ns 执行操作的命名空间。
进一步指定要执行操作的文档。
oplog只记录改变数据库状态的操作。oplog只是作为从节点和主节点保持数据同步的机制。
存储在oplog里的操作不是完全和主节点的操作一模一样的。这些操作在存储之前先做等幂变换,这些操作可以在从服务器端多次执行,只要顺序是对的,就不会有问题。
oplog在固定集合中,不能保证oplog不超过预先设定的大小。需要在创建mongodb服务时指定--oplogSize,参数指定oplog的大小。
一般64bit-linux,分配5%的剩余空间,单位为MB。
9.4.2 同步
从节点第一次启动时,会对主节点数据进行完整的同步。从节点复制主节点上的每一个数据,耗费资源大。同步完成后,从节点查询主节点的oplog,并执行这些操作,保证数据是最新的。
如果从节点的操作被主节点落下太远了,从节点就跟不上同步了,从节点发生宕机或者疲于应付读取时,就会出现这种情况,也会在执行完完整同步后出现这种情况,因为oplog可能已经回滚一圈了。
从节点跟不上同步后,复制就会停下,从节点需要重新做完整的同步。可以用{"resync" : 1}命令手动执行同步,也可以在启动从节点是使用--autoresync选项让其自动同步。重新同步代价高昂,尽量避免,方法就是配置足够大的oplog。
9.4.3 复制状态和本地数据库
本地数据库用来存放所有内部复制状态,主节点和从节点都有。本地数据就是local,其内容不会被复制。可以确保一盒MongoDB数据库只有一个本地数据库。
本地数据库不限于存放MongoDB的内部状态。如果有不想复制的文档,也可以放在本地数据库的集合里。
主节点上的复制状态还包括从节点上的列表。这个列表存放在slaves集合中:
myrepl:PRIMARY& db.slaves.find();
{ "_id" : ObjectId("530bfbdc911eb0ac3bf2aa8b"), "config" : { "_id" : 1, "host" : "test03:10002" }, "ns" : "local.oplog.rs", "syncedTo" : Timestamp(, 1) }
从节点也在本地数据库中存放状态。在me集合中存放从节点的唯一标识符,在sources集合中存放源或节点的列表。
myrepl:SECONDARY& db.me.find();
{ "_id" : ObjectId("530bfbdc911eb0ac3bf2aa8b"), "host" : "test03" }
主节点和从节点都跟踪从节点的更新状况,这个是通过存放在"syncedTO"中的时间戳来完成的。
9.4.4 阻塞复制
开发者可以使用getLastrror的'w'参数来确保数据的同步性。运行getLastError会进入阻塞状态,直到N个服务器复制了最新的写入操作为止。
检查本连接的上一次数据库操作的错误。
myrepl:PRIMARY& db.runCommand("getlasterror")
"lastOp" : Timestamp(0, 0),
"connectionId" : 3525,
"err" : null,
指定"w"选项后,可以使用"wtimeout"选项,表示以毫秒为单位的超时。
阻塞复制会导致写操作明显变慢,尤其是"w"的值比较大时。
9.5.1 管理
MongoDB包含很多有用的管理工具,用以查看复制的状态。
通过 db.printReplicationInfo()命令查看oplog状态。
myrepl:PRIMARY&
db.printReplicationInfo();
configured oplog size:
log length start to end: 1126secs (0.31hrs)
oplog first event time:
Tue Feb 25 :23 GMT+0800 (CST)
oplog last event time:
Tue Feb 25 :09 GMT+0800 (CST)
Wed Feb 26 :23 GMT+0800 (CST)
输出信息包括oplog日志的大小,操作日志记录的起始时间。
查看从库同步状态。
myrepl:PRIMARY& db.printSlaveReplicationInfo();
test03:10002
syncedTo: Tue Feb 25 :09 GMT+0800 (CST)
= 56533 secs ago (15.7hrs)
test01:10003
no replication info, yet.
State: ARBITER
输出信息包括从库的主机名,port信息等。
9.5.2 变更oplog的大小
如果发现oplog大小不合适,最简单的方法就是停掉主节点,删除local数据库的文件,用心的设置重新启动。
# rm -rf /data/node2/local*
为大型的oplog预分配空间非常耗费时间,且可能导致主节点停机时间增加,尽可能的手动预分配数据文件。
9.5.3 复制的认证问题
如果在复制中使用了认证,还需要做些配置,使得从节点可以访问俄主节点的数据。在主节点和从节点都需要在本地数据库增加用户,每个节点的用户名和口令相同。
从节点连接到主节点是,会用存储在local.system.users中的用户认证。最先尝试"repl"用户,如果没有,则用local.system.users中的第一个可用用户。
声明:OSCHINA 博客文章版权属于作者,受法律保护。未经作者同意不得转载。
No tags for this post.
除非注明,本站文章均为原创或编译,转载请注明: 文章来自
分享给朋友:
查看全部:1)">1)">1" ng-class="{current:{{currentPage==page}}}" ng-repeat="page in pages"><li class='page' ng-if="(endIndex<li class='page next' ng-if="(currentPage
相关文章阅读事务的概念事务(Transaction)是并发控制的基本单位...
本教程为 李华明 编著的iOS-Cocos2d游戏开发系列教程:教程涵盖关于i......
专题主要学习DirectX的初级编程入门学习,对Directx11的入门及初学者有......
&面向对象的JavaScript&这一说法多少有些冗余,因为JavaScript 语言本......
Windows7系统专题 无论是升级操作系统、资料备份、加强资料的安全及管......前段时间有个朋友问我,分布式主键生成策略在我们这边是怎么实现的,当时我给的答案是sequence,当然这在不高并发的情况下是没有任何问题,实际上,我们的主键生成是可控的,但如果是在分布式高并发的情况下,那肯定是有问题的。突然想起mongodb的objectid,记得以前看过文档,objectid是一种轻量型的,不同的机器都能用全局唯一的同种方法轻量的生成它,而不是采用传统的自增的主键策略,因为在多台服务器上同步自动增加主键既费力又费时,不得不佩服,mongodb从开始设计就被定义为分布式数据库。下面深入一点来翻翻这个Objectid的底细,在mongodb集合中的每个document中都必须有一个"_id"建,这个键的值可以是任何类型的,在默认的情况下是个Objectid对象。当我们让一个collection中插入一条不带_id的记录,系统会自动地生成一个_id的key&&db.t_test.insert({"name":"cyz"})&&db.t_test.findOne({"name":"cyz"}){&"_id"&:&ObjectId("4df2dcec2cdcd"),&"name"&:&"cyz"&}可以发现这里多出一个Objectid类型的_id,当然了,这个_id是系统默认生成的,你也可以为其指定一个值,不过在同一collections中该值必须是唯一的把 ObjectId("4df2dcec2cdcd")这串值拿出来并对照官网的解析来深入分析。"4df2dcec2cdcd" 以这段字符串为例来进行解析,这是一个24位的字符串,看起来很长,很难理解,实际上它是由ObjectId(string)所创建的一组十六进制的字符,每个字节两位的十六进制数字,总共使用了12字节的存储空间,可能有些朋友会感到很奇怪,居然是用了12个字节,而mysql的INT类型也只有4个字节,不过按照现在的存储设备,多出来的这点字节也应该不会成为什么瓶颈,实际上,mongodb在设计上无处不在的体现着用空间换时间的思想,接下看吧下面是官网指定Bson中ObjectId的详细规范TimeStamp前4位是一个unix的时间戳,是一个int类别,我们将上面的例子中的objectid的前4位进行提取&#dcec&#8221;,然后再将他们安装十六进制专为十进制:&#900&#8221;,这个数字就是一个时间戳,为了让效果更佳明显,我们将这个时间戳转换成我们习惯的时间格式$&date&-d&'&UTC&&&sec'&&-u2011年&06月&11日&星期六&03:11:40&UTC前4个字节其实隐藏了文档创建的时间,并且时间戳处在于字符的最前面,这就意味着ObjectId大致会按照插入进行排序,这对于某些方面起到很大作用,如作为索引提高搜索效率等等。使用时间戳还有一个好处是,某些客户端驱动可以通过ObjectId解析出该记录是何时插入的,这也解答了我们平时快速连续创建多个Objectid时,会发现前几位数字很少发现变化的现实,因为使用的是当前时间,很多用户担心要对服务器进行时间同步,其实这个时间戳的真实值并不重要,只要其总不停增加就好。Machine 接下来的三个字节,就是 2cdcd2 ,这三个字节是所在主机的唯一标识符,一般是机器主机名的散列值,这样就确保了不同主机生成不同的机器hash值,确保在分布式中不造成冲突,这也就是在同一台机器生成的objectid中间的字符串都是一模一样的原因。pid上面的Machine是为了确保在不同机器产生的objectid不冲突,而pid就是为了在同一台机器不同的mongodb进程产生了objectid不冲突,接下来的0936两位就是产生objectid的进程标识符。increment前面的九个字节是保证了一秒内不同机器不同进程生成objectid不冲突,这后面的三个字节a8b817,是一个自动增加的计数器,用来确保在同一秒内产生的objectid也不会发现冲突,允许256的3次方等于条记录的唯一性。客户端生成mongodb产生objectid还有一个更大的优势,就是mongodb可以通过自身的服务来产生objectid,也可以通过客户端的驱动程序来产生,如果你仔细看文档你会感叹,mongodb的设计无处不在的使用空间换时间的思想,比较objectid是轻量级,但服务端产生也必须开销时间,所以能从服务器转移到客户端驱动程序完成的就尽量的转移,必须将事务扔给客户端来完成,减低服务端的开销,另还有一点原因就是扩展应用层比扩展数据库层要变量得多。好吧,既然我们了解到我们的程序产生objectid是在客户端完成,那再继续,进一步了解,打开mongodb java driver源码,无源码可以到mongodb官网进行下载,下面摘录部分代码public&class&ObjectId&implements&Comparable&ObjectId&&,&java.io.Serializable&{&&&&final&int&_&&&&final&int&_&&&&final&int&_&&&&public&ObjectId(&byte[]&b&){&&&&&&&&if&(&b.length&!=&12&)&&&&&&&&&&&&throw&new&IllegalArgumentException(&"need&12&bytes"&);&&&&&&&&ByteBuffer&bb&=&ByteBuffer.wrap(&b&);&&&&&&&&_time&=&bb.getInt();&&&&&&&&_machine&=&bb.getInt();&&&&&&&&_inc&=&bb.getInt();&&&&&&&&_new&=&false;&&&&}&&&&&&&&public&ObjectId(&int&time&,&int&machine&,&int&inc&){&&&&&&&&_time&=&&&&&&&&&_machine&=&&&&&&&&&_inc&=&&&&&&&&&_new&=&false;&&&&}&&&&&&&&public&ObjectId(){&&&&&&&&_time&=&(int)&(System.currentTimeMillis()&/&1000);&&&&&&&&_machine&=&_&&&&&&&&_inc&=&_nextInc.getAndIncrement();&&&&&&&&_new&=&true;&&&&}(完整代码请查看源码)这里可以发现ObjectId的构建可以有多种方式,可以由自己制定字节,也可以指定时间,机器码和自增值,这里重点看看驱动程序默认的构建,也就是public ObjectId()可以看到objectid主要由_time _machine _inc 所组成,其中 _time直接由(System.currentTimeMillis() / 1000)计算出所谓的时间戳,这里很简单,接下来是重点,主要看看机器码和进程码的构建&private&static&final&int&_&&&&static&{&&&&&&&&try&{&&&&&&&&&&&&final&int&machineP//机器码块&&&&&&&&&&&&{&&&&&&&&&&&&&&&&StringBuilder&sb&=&new&StringBuilder();&&&&&&&&&&&&&&&&Enumeration&NetworkInterface&&e&=&NetworkInterface.getNetworkInterfaces();//NetworkInterface此类表示一个由名称和分配给此接口的&IP&地址列表组成的网络接口,它用于标识将多播组加入的本地接口,这里通过NetworkInterface此机器上所有的接口&&&&&&&&&&&&&&&&while&(&e.hasMoreElements()&){&&&&&&&&&&&&&&&&&&&&NetworkInterface&ni&=&e.nextElement();&&&&&&&&&&&&&&&&&&&&sb.append(&ni.toString()&);&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&machinePiece&=&sb.toString().hashCode()&&&&16;&//将得到所有接口的字符串进行取散列值&&&&&&&&&&&&&&&&LOGGER.fine(&"machine&piece&post:&"&+&Integer.toHexString(&machinePiece&)&);&&&&&&&&&&&&}&&&&&&&&&&&&final&int&processP//进程块&&&&&&&&&&&&{&&&&&&&&&&&&&&&&int&processId&=&new&java.util.Random().nextInt();&&&&&&&&&&&&&&&&try&{&&&&&&&&&&&&&&&&&&&&processId&=&java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode();//RuntimeMXBean是Java虚拟机的运行时系统的管理接口,这里是返回表示正在运行的&Java&虚拟机的名称,并进行取散列值。&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&catch&(&Throwable&t&){&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&ClassLoader&loader&=&ObjectId.class.getClassLoader();&&&&&&&&&&&&&&&&int&loaderId&=&loader&!=&null&?&System.identityHashCode(loader)&:&0;&&&&&&&&&&&&&&&&StringBuilder&sb&=&new&StringBuilder();&&&&&&&&&&&&&&&&sb.append(Integer.toHexString(processId));&&&&&&&&&&&&&&&&sb.append(Integer.toHexString(loaderId));&&&&&&&&&&&&&&&&processPiece&=&sb.toString().hashCode()&&&0xFFFF;&&&&&&&&&&&&&&&&LOGGER.fine(&"process&piece:&"&+&Integer.toHexString(&processPiece&)&);&&&&&&&&&&&&}&&&&&&&&&&&&_genmachine&=&machinePiece&|&processP&//最后将机器码块的散列值与进程块的散列值进行位或运算,得到&_genmachine&&&&&&&&&&&&&LOGGER.fine(&"machine&:&"&+&Integer.toHexString(&_genmachine&)&);&&&&&&&&}&&&&&&&&catch&(&java.io.IOException&ioe&){&&&&&&&&&&&&throw&new&RuntimeException(&ioe&);&&&&&&&&}&&&&}&Enumeration&NetworkInterface& e = NetworkInterface.getNetworkInterfaces();&&&&&&&&&&&&&& while ( e.hasMoreElements() ){&&&&&&&&&&&&&&&&&&& NetworkInterface ni = e.nextElement();&&&&&&&&&&&&&&&&&&& sb.append( ni.toString() );&&&&&&&&&&&&&&& }&machinePiece = sb.toString().hashCode() && 16;这里的NetworkInterface.getNetworkInterfaces();取得的接口通常是按名称(如 "le0")区分的,大约是下面的类型name:lo&(Software&Loopback&Interface&1)&index:&1&addresses:/0:0:0:0:0:0:0:1;/127.0.0.1;name:net0&(WAN&Miniport&(SSTP))&index:&2&addresses:name:net1&(WAN&Miniport&(IKEv2))&index:&3&addresses:name:net2&(WAN&Miniport&(L2TP))&index:&4&addresses:name:net3&(WAN&Miniport&(PPTP))&index:&5&addresses:name:ppp0&(WAN&Miniport&(PPPOE))&index:&6&addresses:这里为什么要采取这样方面进行取散列值,感觉有些不太理解,应该网络接口本身相对而言是并不稳定的int processId = new java.util.Random().nextInt();&try {&&&&&&& processId = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode();&}&catch ( Throwable t ){}RuntimeMXBean是Java虚拟机的运行时系统的管理接口,这里是返回表示正在运行的 Java 虚拟机的名称,并进行取散列值,如果在这过程中出现异常,processId 将以随机数的方式继续计算_genmachine = machinePiece | processP最后将机器码块的散列值与进程块的散列值进行位或运算,当然这里是十进制,你把这里的十进制专为十六进制,就会发现这块的值就是生产objectid中间部分的值,这里的构建跟服务端的构建是有些不一样的,不过最基本的构建元素还是一致的,就是TimeStamp,Machine ,pid,increment。mongodb的ObejctId生产思想在很多方面挺值得我们借鉴的,特别是在大型分布式的开发,如何构建轻量级的生产,如何将生产的负载进行转移,如何以空间换取时间提高生产的最大优化等等。----------------------------------------
Mail: dongbule&#
guid足以,就是解决多节点问题的。&&&&
&re: 说说MongoDB的ObjectId
&#64;&#64;joeguid虽然好处多多,但存储大,索引也慢,还有不美观&&&&
&re: 说说MongoDB的ObjectId
不错,不错,&&&&
&re: 说说MongoDB的ObjectId
分析得很不错&&&&
&re: 说说MongoDB的ObjectId[未登录]
讲的非常详细,蛮不错&&&&
&re: 说说MongoDB的ObjectId
&#64;陈于喆ObjectId也有&存储大 索引慢 不美观&这些缺点啊 它就是一简化版GUIDGUID是全球唯一的 ObjectID是特定DB集群内唯一的&&&&
&re: 说说MongoDB的ObjectId
&#64;习惯性路过恩,有道理,那你说说为什么mongodb不直接使用GUID&&&&
&re: 说说MongoDB的ObjectId
&#64;陈于喆一个可以逆向推算出数据对应的( 插入时间,插入的机器),而GUID一个不能&&&&

我要回帖

更多关于 mongodb timestamp 的文章

 

随机推荐