踩了 MongoDB 的一个小坑

2015-05-15 14:51:19 +08:00
 sing1ee

先了解几个基本点:
1. MongoDB默认生成的ObjectId是在客户端完成的,时间戳+host+进程id等,详情见文档
2. ObjectId生成的时候,是根据当期那服务器时间所在的时区
3. 从collection中读出ObjectId,读取generation_time属性,默认是UTC
4. 北京时间比UTC时间早了八个小时

我们两台服务器,一个是北京时间,一个比北京时间早了八个小时。。。从这两个服务器入库MongoDB,并且根据generation_time读,就彻底乱了,结果
1. 北京时间的服务器,得到的generation_time时间比北京时间晚了八个小时
2. 比北京早了的服务器,得到的generation_time时间和北京时间相同

我们以为2是正确的。。。

我们用的是pymongo

5237 次点击
所在节点    程序员
21 条回复
ZackYang
2015-05-15 15:13:35 +08:00
我都是用 uuid 改写...
jiangzhuo
2015-05-15 15:14:25 +08:00
做集羣 肯定要集羣間使用的時間統一的,特別是這種生成唯一性和時間有序的id的時候
yueyoum
2015-05-15 15:19:23 +08:00
这点还真没注意。

我现在基本都用 UUID 做主键。 无论是 mysql, postgresql 还是 mongodb

简单,方便。 uuid4 冲突几率是有, 如果你能碰到, 也算是你的运气。

什么 分布式, 唯一ID生成, 数据库合并, 瞬间解决。



特别方便的一个使用例子是 django orm 的 bulk_create, 如果你的主键是 自增长 int
那么 返回的 列表中 对象是没有 id 属性的。

这时候用UUID 先给对象赋予ID, bulk_create 返回的对象就是带有ID属性的。

这在 bulk_create后, 需要这些新增加对象ID 的情况 特别有用。


再说一个场景吧。

曾经有个项目, 一个server 对应一个 db, 用的 mongodb,

因为担心效率问题,没有上全局ID生成服务。

是每个server自己算的, 算法大概是 PARAM * NEWID + SERVER_ID

param 是一个定义好的数值,比如 1024,
newid 是这个server 自己生成的唯一自增长ID, 比如 1,2,3,4...
server_id 就是这个 server 的 id, 不同server 的 id 不一样。

这样生成好处是 方便,快速
但缺点也很明显

能开多少server 是由 param 来决定的, 最多 param 个



所以,上UUID, 上面问题一锅端。


有同学会说 INT ID 好啊, 数字的,自然就排序了,
mongo 的 objectid 好啊, 还带有时间戳, 也可以排序。


大不了在记录中增加一个 create_at 字段就行,
相比上面的问题, 增加一个带索引的列,根本不是问题
kslr
2015-05-15 16:09:02 +08:00
这不是mongodb的坑,这么多机器时间还不统一,另外 create_at和update_at
cloudzhou
2015-05-15 19:38:12 +08:00
@yueyoum 但是使用 uuid 会不会引起性能的减退呢,字段这么长
sing1ee
2015-05-15 19:48:06 +08:00
@kslr 时间同步,有什么好方案么
likuku
2015-05-15 20:21:16 +08:00
@sing1ee 配置好 ntpd 服务啊,每台服务器都运行它。ntpd 是渐进式校准服务器时钟,不会引起时钟误差颠簸(有些服务/软件假若侦测到时钟颠簸,可能会终止运行/运行不正常)。
likuku
2015-05-15 20:22:45 +08:00
@sing1ee 关于时钟颠簸/跃变 会引起问题,参考:

AsiaBSDCon上说OpenBSD的sensor framework的时候的一个观点 - delphij's Chaos : https://blog.delphij.net/2007/03/asiabsdconopenb.html
mko0okmko0
2015-05-15 22:08:26 +08:00
统一使用格林威治时间.直接存成秒数字.一般索引提速.需要时间索引则另用函数生成索引.
springwarm
2015-05-15 23:20:41 +08:00
楼主给出的基本点,"北京时间比UTC时间早了八个小时",会不会有问题

UTC 和本地时间的换算公式是:
UTC + 时区差 = 本地时间

北京是东八区,对应的公式应该是:
UTC + (+0800) = 北京时间

以此推断,是UTC 时间比北京时间早了八个小时吧
sing1ee
2015-05-15 23:54:36 +08:00
@springwarm 这个应该是北京早吧=;=
Landarky
2015-05-16 09:31:43 +08:00
不仅是时区问题 机器时间也可能差几分钟 修改到一致就好
likuku
2015-05-16 11:11:00 +08:00
@Landarky 修改机器时间...都21世纪了,没啥理由不用ntpd吧。如今桌面的 ubuntu/osx/windows都默认安装并开启ntpd服务的了。
xiaogui
2015-05-16 12:42:09 +08:00
UTC 并不是问题,都采用 UTC 最好了。
whatisnew
2015-05-16 13:13:12 +08:00
我用 mongodb 试着删除 where asc 顺序的前 30 行记录,折腾了半小时 remove findandmodify 都没搞定,然后,我就撤退了。。。
makuta
2015-05-16 17:31:13 +08:00
做Mongo集群
ddou
2015-05-16 18:57:37 +08:00
个人觉得是使用方式不对,ObjectId当ID用就行了,其他时间的话应该是CreatedAt和UpdatedAt
Cu635
2015-05-16 21:55:13 +08:00
@springwarm 北京时间比UTC早。UTC的1:00am是北京时间当天的9:00am。
Cu635
2015-05-16 21:57:00 +08:00
@springwarm 计算公式是
1:00am(UTC)+(+0800)(东八区)=9:00am(北京时间,东八区时间)
VirgilMing
2015-05-17 01:27:20 +08:00
我很好奇哪个时区比 UTC+8 还 +8?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/191317

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX