V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
DovaKeen
V2EX  ›  问与答

关于 MongoDB 实现计数器时并发计数错误的问题

  •  
  •   DovaKeen · 2019-04-22 22:00:55 +08:00 · 2574 次点击
    这是一个创建于 2076 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我用 MongoDB 的 upsert 和$inc 实现了一个简单的自增计数器,但是却发现当对一个键并发自增计数时,会丢失数据,让人百思不得其解啊

    我的 MongoDB 进程是在本机运行的,计数器主要代码如下: 计数器的 incr 实现:

    public boolean incr(String key) {

        checkKey(key);
        
        UpdateResult result = mongoTemplate.upsert(query(Criteria.where("key").is(key)),
                new Update().inc("value", 1),
                MongoCounter.class, COLLECTION_NAME);
        return result.wasAcknowledged();
    
    }
    

    测试代码:

        int count = 200;
        ExecutorService executor = Executors.newCachedThreadPool();
        String key = "incrkey";
        for (int i = 0; i < count; i++) {
            executor.submit(() -> counter.incr(key));
        }
        assertTrue(counter.getCounter(key) == count);
        executor.shutdown();
        counter.delCounter(key);
    

    在测试时,开了一个线程池,用多个线程同时向一个初始没有值的键连续调用 n 次 incr,但是结果发现总是会丢失几次更新,难道原子性更新的前提是并发量不能太大吗?

    请懂 MongoDB 的人指点一二啊

    第 1 条附言  ·  2019-04-23 08:58:18 +08:00
    assertTrue 这条语句无法通过,并且数据库中的数据也确实不对
    4 条回复    2019-04-23 15:46:54 +08:00
    xmh51
        1
    xmh51  
       2019-04-22 22:20:04 +08:00
    不应该立马 executor.shutdown();
    你等等 java 执行完异步线程再关闭.........
    DovaKeen
        2
    DovaKeen  
    OP
       2019-04-23 08:51:31 +08:00
    @xmh51 executor.shutdown();执行之后,已经提交过的任务还是会跑完才关闭的,不会直接关闭线程池呀,而且我注释掉了这一句后,还是会丢失更新,,
    xmh51
        3
    xmh51  
       2019-04-23 10:22:39 +08:00
    @DovaKeen 用错了方法你 This method does not wait for previously submitted tasks to
    * complete execution 该方法会立即返回, 但不一定代表之前提交的任务已经全部完成了. 如果需要一个阻塞的方法, 可以调用 awaitTermination 方法
    DovaKeen
        4
    DovaKeen  
    OP
       2019-04-23 15:46:54 +08:00
    @xmh51 确实是这里的问题,并且还发现当多个线程同时对一个没有值的键调用 $inc 时,可能会产生多个值为 1 而键名相同的文档,这里也是会导致计数器结果错误的地方,现在给键名加上 unique 约束,并且在调用后捕获异常重设值,就可以得到正确的结果了。谢谢你呀
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2621 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 05:18 · PVG 13:18 · LAX 21:18 · JFK 00:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.