MongoDB数据量大于2亿后遇到的问题 及原因分析

一、数据增长情况

  1. 每月增长量最大达到了1.9亿,每天增长约300W-500W

二、遇到的情况及解决方法

数据量过大,并且都集中在一个表,所以此表数据插入变慢。表索引越多越明显,

优化处理方法:

  • 优化索引,以前的startTime日期字段索引,修改为客户端用日期生成ObjectId,再用_id 来进行查找。

  • TraceId 字段(一个TraceId 对应多条记录)计划也删除,后面再用ES 系统先查询到_id 后,再从mongoDB 进行查找。

原因分析:

  • 当表数据增长到千万级后,内存数据中的索引数据增加,内存越来越不够用,需要刷新脏数据增多,mongostat 分析的 dirty % > 3,后从16G 内存升级到32G 内存后,情况稍有好转。

数据量过大后,从节点时尔出现CPU load 负载过高,从节点尤其明显。

在把表重命名,新数据插入到新表处理后:

  1. ` db.TraceLogs.renameCollection("TraceLogs_bak170210");`
  2. (新数据插入时,会自动生成表TraceLogs

历史数据表统计信息

  1. Log:PRIMARY> db.TraceLogs_bak170210.stats()
  2. {
  3. "ns" : "RavenLogs.TraceLogs_bak170210",
  4. "count" : 384453917,
  5. "size" : 865594958942,
  6. "avgObjSize" : 2251,
  7. "storageSize" : 444,613,255,168,
  8. .....
  9. "nindexes" : 2,
  10. "totalIndexSize" : 15275057152,
  11. "indexSizes" : {
  12. "_id_" : 3,973,029,888,
  13. "TraceId_1" : 11,302,027,264
  14. },
  15. "ok" : 1
  16. }

从此统计信息中可以看到:

表存储大小: 444G, 索引 id 3.9G, TraceId_1 大小:11G

再次查看数据库性能从以前的:

  1. load average: > 5.47, 5.47, 5.47

降到了:

load average: 0.88, 1.34, 1.69

(主从节点,皆已下降)

在做历史数据迁移期间,又升到了> 8 并且时频繁出现。

完成数据迁移后,回落到 2 < load avg< : 4 之间 (升级到MongoDB3.4 之后)

原因分析:

个人认为,主因还是因为内存不够。索引+热数据远远超过了16G的MongoDB使用内存。从而导致大量的IO,相对的CPU load 也上去了。

在把原大表TraceLogs 改名后(TraceLogs_bak170210),大量的热块数据已被清除出内存,

3.此前数据库从节点内存升级后(16G —> 32G),参数配置不当,节点实例当机情况:

wiredTiger:

engineConfig:

cacheSizeGB: 28 (限制mongoDB 使用内存最大值)

后调整为默认值

  1. `#cacheSizeGB: 28` (限制mongoDB 使用内存最大值),默认值为50%
  2. mongoDB实例恢复正常,但CPU load 也一直居高不下。

原因分析:

系统使用内存太少,可能是磁盘缓存过低,而无法读写数据,但在mongoDB 日志中,无法找到原因。只是看到实例被关闭。

4. 因为oplog 同步表最大设置值(oplogSizeMB)为50G, 但50G 只能保存52h 的数量变化量。

想添加新的从节点时,当同步完成数据后,已过了oplog 的窗口期.

oplogSizeMB的大小要大于数据同步完成+索引建立完成的时间段内生成的数据量,

当同步完成后,从节点从主节点读oplog表的数据,发现最小的同步时间,已大于从节点中同步开始时的时间了,这就是窗口期已过期)

数据量大后,重新创建索引的时间特别惊人,一个索引需要10多个小时。

500G 存储量,总共需要3天左右的数据完成节点的数据同步及索引创建。

后面计划在添加节点前,做以下调整:

  • 把数据库升级到3.4 版本,此版本在新节点数据同步,创建索引上,号称已有很大的改善。

  • 删除能够优化的索引,如果索引无法优化,也可以考虑先把某个索引删除,节点完成后,再重新建立

经验总结:

  • 索引的优化,尽可能的发挥主键索引的功能,比如上面说到的,使用日期范围自己生成_id 范围,用_id字段进行查询,能不建立索引,就不建立。在大增长的表中,极其重要。

  • 数据库服务器的内存配置上,内存>索引大小,或者是配置到 内存>=索引大小+热数据大小 还是有必要的。

  • 数据库服务器的磁盘配置上,如果是云服务器,尽量采用高效云盘。使用EXT4,或者使用NFS 格式也是有必要的。

  • 如果一个库有多个表的数据达到亿级时,可能也是考虑使用分片集群的时候,特别是如果此表是做为主业务 数据库的情况。

mongoDB 从单节点迁移到分布式集群 遇到的问题:

  1. 1.linux 环境下limit 的设置问题:
  2. 错误:
  3. $ ps -ef|grep mongod
  4. -bash: fork: retry: 资源暂时不可用
  5. -bash: fork: retry: 资源暂时不可用
  6. ^C-bash: fork: retry: 资源暂时不可用
  7. -bash: fork: retry: 资源暂时不可用
  8. -bash: fork: 资源暂时不可用
  9. 原因:
  10. linux 默认用户打开文件数为1024,进程数也是1024
  11. 所以当客户端连接超过了,就出错了。
  12. 解决方法:重新设置ulimit ,以解决mongod 自动关闭。
  13. 修改文件:
  14. /etc/security/limits.conf
  15. 比如添加以下内容:
  16. mongo soft nproc 30000
  17. mongo hard nproc 65536
  18. mongo soft nofile 30000
  19. mongo hard nofile 65536
  20. 2.mongodump不能先锁表。
  21. db.fsyncLock() #锁住表
  22. mongodump
  23. db.fsyncUnLock() #解锁
  24. 以上方法 mongodump 无法运行,也无法备份数据(停住了)
  25. 这个是我突然想当然的,试试是否在备份时也可以先锁住表,不让客户端写操作。就出这问题了。
  26. 3.数据备份请请确认索引是否有与分片索引相同,有相同的,先删除索引后,再备份:
  27. 在导入数据时,看到有一个表原先有两个字段的复合索引,
  28. 在选择片键时,也是这两个字段做的片键,数据导入时,提示索引无法更新,出错。
  29. 解决方法:删除原索引,或是修改片键值。
  30. 4.Hash索引时对key进行hash计算然后创建索引,目前只支持等号运行,不支持区间
  31. 使用了HASH 片键后,查询/更新,不能使用 in, $gte $lte 之类的条件。
  32. 所以如果你客户端有太多的代码是使用了 in, $gte $lte 之类的查询条件,那要
  33. 权衡一下了。你的片键选择是否得当,或是修改代码。
  34. 5.当更新单个文档如果不指定片键,会出以下错误:
  35. does not contain shard key for pattern { user_id: 1.0, cookie: 1.0 }'
  36. MongoDB更新操作无法确定更新到具体的某个分片上,所以出错,
  37. 指定片键后,更新操作只会路由到某一个分片上。
  38. 但如果你是更新多条记录,路由会更新到所有分片上。
  39. 6.最大连接数问题
  40. mongodb 默认最大连接数为928,如果你没有指定,到了这个数时,mongos,或是mongodb进程
  41. 会自动关闭,如果查看日志也无法确认是什么原因mongod 服务器突然自动关闭了,那可以查查
  42. 当前连接数是多少,
  43. mongos> db.serverStatus().connections;
  44. {
  45. "current" : 1240,
  46. "available" : 22760,
  47. "totalCreated" : NumberLong(189438)
  48. }