ma6174 / blog

博客
https://ma6174.github.io/blog/
138 stars 18 forks source link

mongodb几个坑 #3

Open ma6174 opened 10 years ago

ma6174 commented 10 years ago

使用mongodb有一段时间了,总体来说,mongodb还是非常给力的,但是有几个问题也是需要注意的。

对非UTF8编码的KEY支持不好

理论上说在数据库中,一个主键(单一主键或者联合主键)在数据库中的值是唯一确定的。在一次倒数据时发现对于同一个主键(存在非UTF8编码字符)在数据库中存在多个值,并且值还是不完全一样的。使用数据库API查询的时候当然只能查到一个值,但查到的值不一定是最新的,可能会导致数据不一致现象。因此在数据库存储过程中最好不要使用非UTF8编码的KEY

主从数据库数据不一致

mongodb使用复制集的时候,可以指定一个主数据库,接收所有的写请求(包括修改),然后记录相应的操作日志即oplog,从数据库从主数据库拉取oplog,再按先后顺序重放一下oplog,最终主数据库和从数据库数据应该是一致的。

最近在倒数据偶然发现主数据库和从数据库数据条目数不一致,表现为所有从数据库数据条目数比主数据库少并且所有从数据库数据条目数是一致的,数据正确性还需要进一步验证。主数据库在几天前已经停止写入和修改数据库了,从数据库已经同步完成了,从数据库条目数一致可以从反面印证。后来经过精确主从数据库数据对比发现有些数据只在主数据库有,有些数据只在从数据库有,有些数据在两个数据库都有但是内容不一致,最奇怪的一条是从数据库中的记录少了几个字段。后来查询数据库详细日志发现从数据库有恢复迹象,当数据文件损坏的时候数据库会进行数据修复。目前的问题是一个从数据库修复会影响所有的从数据库导致主从不一致。当磁盘出现问题的时候要格外关注,操作不当会导致丢数据,需要慎重。

大量数据导入速度缓慢

当数据量达到亿级别以后,从数据源倒数据到mongodb性能会快速下降。有一张tokumx和mongodb的对比图可以看出来随着数据量的增加mongodb写入性能下降非常快。 http://www.tokutek.com/wp-content/uploads/2013/06/mongodb-blog-09-iibench-tps.png 我曾经测试过几种导入方式,比如单条写入、批量写入、使用mongoimport或mongorestore等官方工具导入,速度都不理想,导入速度越来越慢,可能和需要创建索引有关。sharding数据库导入速度更慢,估计和mongodb在分片的时候平衡数据有关。

因此需要对数据的量最好预估,不要在磁盘满的时候再想换大磁盘,不要等数据放不下的时候再做分片。

另外tokumx数据库看起来性能比mongodb好很多,还支持数据压缩,能节省很大的磁盘空间,并且是基于mongodb改造的,大部分api接口都是兼容的,可以做进一步的测试。

defp commented 10 years ago

tokumx试过吗

ma6174 commented 10 years ago

@lidashuang 有详细测试过,目前看是非常给力,性能比mongodb高了一大截,存储空间也比mongodb小了很多。

idning commented 10 years ago

不一致可能是replset 发生选举的时候导致的? 一般每次选举丢2-10s的写入.

我们做过大量导入, 目前没看到你说的导入变慢的情况.

toku也有坑:)

ma6174 commented 10 years ago

@idning 后来查明发现是因为一个slave数据库文件损坏丢失了数据,其他slave数据库中的数据也丢失了,所有slave中的数据一致,但是master和slave出现不一致现象。后来经过检查发现master中的数据是正确的。

您做大量导入,我想了解一下您的量是多大?以什么方式导入的?

tokumx您遇到过哪些坑?能否分享一下?

idning commented 10 years ago

我用 mongoimport 做导入, 一般限制在单replset 导入速度2000/s, 超过2000就会导致主从同步跟不上.

我们导入数据是为了在多个集群之间做迁移 (https://github.com/idning/mongomigrate)

我们一般用多个worker 每个全速用safe方式导入(这里原生的mongoimport 是非safe 导入的, 也就是说在导入过程中, 如果发生主动切换, 丢数据是不可知的, 我们做了修改, 每次insert后调用getlasterrror(), 所以单线程大约500/s),

这么说来,我们没遇到你描述的'导入变慢', 是因为我们 本来就很慢 :)

tokumx 的坑主要是它的oplog不是capped collection, oplog 过期后需要逐条删除, 这个删除的时候,对线上影响很大. 特别是在update多, 或者record 大的情况下.

ma6174 commented 10 years ago

@idning 我们导入数据主要是做几个数据库的数据合并,因为数据量很大,十亿级别的,所以对数据导入的速度要求比较苛刻,否则导入速度跟不上数据产生的速度就麻烦了。

replset方式同步数据会拖慢导入速度,因此我们在导入数据的时候一般是只启动一个数据库实例,导入完成之后直接拷贝多份就可以组成replset。

你说的使用safe方式导入其实是可以优化的:采用批量写入,写入之后调用getlasterrror(),如果有错误,因为只有最后一条错误,不能确定是哪个数据insert出错,因此再对刚才批量导入的数据执行一次safe insert,没有错误则继续批量写入,毕竟切换出现的概率是比较小的,这样可以提高写入速度。

我说的导入变慢是因为随着数据库中数据条目数的增加,每次insert都需要首先查询一下数据在数据库中是否存在,数据量越大查询越慢,会导致导入越慢。当然如果没有查询这个操作的话,导入速度是比较稳定的,不会出现所谓的变慢现象。

您说的那个oplog的坑我们要考察一下,感谢您的分享。

idning commented 10 years ago

我们也是在十亿级别, 我们是导入到一个sharding+replset的线上集群,你们只用一个Mongod实例来做导入, 就不存在replset主从切换的问题, 确实可以用最大速度导入. 我们提高速度是靠并发, 但是最终还是受限在主从同步上.

你们的思路很赞啊, 呵呵

ma6174 commented 10 years ago

@idning 之前也尝试过使用sharding方式导入,速度还不如replset方式快,毕竟sharding多了一个mongos,还涉及到分片和均衡。今天查了一下官方文档说可以启动多个mongos,然后导入数据也通过多个mongos来导入,这样mongos的压力应该能降下来,可能速度会有增加,不过我没测试过。

关于tokumx的oplog看了一下文档,从1.4版本开始使用的是partitioned collection

In TokuMX 1.4.0 the oplog is now a “partitioned” collection, which is also a new type of collection, similar to SQL partitioned tables. A partitioned collection is represented on disk by a set of normal collections, together with some metadata that specifies which data is in which collection.

There are currently many limitations for partitioned collections, so they probably aren’t useful for most applications yet, but they can be used by normal applications, and they’ll become more useful in the future. The current limitations include:

  • Secondary indexes aren’t supported, they must use the default “_id” index as the only index, and they must be partitioned according to the _id field (and they cannot use primary keys).
  • Partitioned collections can’t be renamed.
  • The addPartition/dropPartition commands cannot be replicated, so they should not be used in a replica set outside of the local database.

For the oplog, each day a new partition is added and the oldest partition is dropped, which quickly and easily reclaims the space used by the oldest partition, without consuming extra resources or blocking other operations the way trimming could in the past. This will make cluster administration much simpler with respect to the oplog, for many users.

看上面的描述,tokumx的oplog存储在一个分区的集合里面,每天新分区被添加,旧分区被删除这样迭代。1.5版本貌似还有增强:https://github.com/Tokutek/mongo/releases/tokumx-1.5.0

mongodb主从同步是通过查oplog的时间戳:

Tue Jun 17 22:50:47.027 [conn85] getmore local.oplog.rs query: { ts: { $gte: Timestamp 1402945881000|4 } } cursorid:629533960084194297 ntoreturn:0 keyUpdates:0 numYields: 565 locks(micros) r:677931 nreturned:75252 reslen:1279304 388ms

tokumx改成了根据_id来查询:

Thu Jul 17 10:52:35.058 [conn5] getmore local.oplog.rs query: { query: { _id: { $gte: BinData(0, 00000000000000010000000000000000) } }, $hint: { _id: 1 } } cursorid:558064340525120 ntoreturn:0 keyUpdates:0 locks(micros) r:146449 nreturned:13075 reslen:4194567 146ms

上面两个是从日志中看到的慢请求。

这里还有一篇文章讲用TokuMX Partitioned Collections替换TTL Indexeshttp://www.tokutek.com/2014/06/use-tokumx-partitioned-collections-in-place-of-ttl-indexes/

我在tokumx1.5中测试7000~8000左右的insert,主从同步基本没受到影响。您说的“oplog 过期后需要逐条删除”这个不太明白,按tokumx不是删除一个旧的partition就好了吗,为什么是逐条删除?或者是tokumx的版本不同?还有update多, 或者record 大的情况

idning commented 10 years ago

1.3 是逐条删, 1.4改成了一天一个collection.

我们还在用1.3, 1.4 以后就没有这个问题了, 不过我们这里没有试过.

jingjingdba commented 3 years ago

大佬您好,方便加个微信不

wangmyqs commented 3 years ago

---原始邮件--- 发件人: @.> 发送时间: 2021年10月26日(周二) 下午2:11 收件人: @.>; 抄送: @.***>; 主题: Re: [ma6174/blog] mongodb几个坑 (#3)

大佬您好,方便加个微信不

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android.