• mongodb中出现_id字段重复记录的排查笔记


    近期在使用mongodb的过程中遇到一次表中有几百条_id字段重复的记录(相同_id的有两条),着实吓了一大跳,因为_id字段在mongodb里面已经默认创建了唯一索引,理论上是不可能有重复记录的,因此特把排查过程记录下来。

    1. 问题定位

        发现这个现象,是在定位一个问题的时候,发现了这批重复脏数据,bug出现的步骤:把一条记录中的某个字段修改后,再执行save方法,由于修改的字段是shard key,且保存的时候路由到另外一组shard(和原记录的shard不同),导致了重复_id的出现。

    2. 问题复现

    首先,准备测试元数据,插入脚本如下:

    db.auth("test","test");
    var total = 500;
    var page = 1000;
    for(i=1; i<=total; i++){
        for(var j 0= 1; j <= page; j++){
         db.users.save({'_id': 'user'+i+"-"+j,'age':j,"content":"012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901223456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567823456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"});
        }
    }

    其中content字段的内容很长,1000多个字符,这样50w条的数据量是满足分片后的数据迁移条件的(数据量太小,mongodb是不会迁移的)。

    把该文件保存在mongo可执行程序的目录,再执行数据插入:

    /mongo 127.0.0.1:30000/test saveUser.js

    随后对test集合创建索引,并进行分片:

        db.users.createIndex({"age":1})
    sh.enableSharding("test") sh.shardCollection("test.user", { age: 1 } )

    等待分片数据迁移结束后,查看分片状态:

    sh.status()

    user表的分片数据如下:

    {  "_id" : "test",  "primary" : "rep1",  "partitioned" : true }
                    test.user
                            shard key: { "age" : 1 }
                            unique: false
                            balancing: true
                            chunks:
                                    rep1    14
                                    rep2    11
                            too many chunks to print, use verbose if you want to force print

    基础数据已经准备完毕了,下面开始造数据,首先查询到第一条记录内容如下:

    { 
        "_id" : "user1-1", 
        "age" : 1.0, 
        "content" : "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901223456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567823456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
    }

     然后把该记录的内容拷贝一份,并把age修改为1000,然后再保存到users集合中:

    
    
    MongoDB Enterprise mongos> db.users.save({ "_id" : "user1-1", "age" : 1000.0, "content" : "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901223456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567823456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" })

    此时_id为“user1-1”的记录已经有两条了:

        MongoDB Enterprise mongos> db.users.find({"_id":"user1-1"})
    { "_id" : "user1-1", "age" : 1000, "content" : "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901223456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567823456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" }
    { "_id" : "user1-1", "age" : 1, "content" : "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901223456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567823456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" }

    3. 避免措施

    从以上分析可知,在对分片集合进行修改操作或者新写入操作时,要特别注意,由于shard key的路由问题,可能会导致_id字段或者其他唯一字段重复记录(保存在不同的shard中),为了避免重复记录,选择shard key时,可以把唯一字段也加入到shard key中,以本次测试为例,shard key可以设置为{"age":1, "_id":1},如果不想把_id加入到shard key中,且业务上面不允许_id重复,则需要在写入前先执行查询。

  • 相关阅读:
    微软校园招聘  研发工程师A
    版本号排序
    腾讯2018校园招聘  研发工程师笔试题(三)
    好词好句
    HikariCP 连接最快的连接池
    Ubuntu 18.04.1 安装java8
    Ubuntu 18.04.1 安装mysql 5.7.27
    markdown 插入链接
    面试题 int(3) int(10) 区别
    采购单(京东2017秋招真题)
  • 原文地址:https://www.cnblogs.com/xinghebuluo/p/12623311.html
Copyright © 2020-2023  润新知