Skip to content

java mongo summary

landon edited this page Sep 14, 2018 · 1 revision

Java项目中mongo问题总结

理解数据库(mongo/mysql)

  1. 数据库本身是一个服务,对外提供网络服务,监听端口
  2. 客户端访问数据库是一个c/s,socket交互
  3. 数据库驱动本身实现可以是同步,可以是异步(网络层实现)
    • 同步的实现可以简单理解为一个连接一个线程处理
    • 异步的实现可以简单理解为一个线程可以处理多个连接
    • 扩展:nginx与apache,tomcat与netty,unix 5种i/o模型
  4. 客户端访问则通常要设置数据库连接池
    • 最小、最大连接数
    • 最大空闲数
    • 超时
  5. 数据库的瓶颈
    • 主要是cpu(不考虑磁盘和内存)
    • 所以数据库的线程数目设置很重要
    • 需要调优测试,达到数据库线程和最大连接数的一个平衡
    • 那么客户端超时也很好理解了,数据库肯定非常繁忙(拳皇海外的问题)(参考游戏客户端的‘延迟’)
  6. 最佳客户端实践
    • You want a small pool, saturated with threads waiting for connections
    • 你需要一个小连接池,和一个充满了等待连接的线程的队列
    • 你需要一个10来个连接的小连接池,然后让剩下的业务线程都在队列里等待。连接池中的连接数量应该等于你的数据库能够有效同时进行的查询任务数

Java项目使用mongo遇到的问题总结

  1. 问题1:压测登录时出现异常
    • com.mongodb.MongoWaitQueueFullException: Too many threads are already waiting for a connection. Max number of threads (maxWaitQueueSize) of 5 has been exceeded.
    • com.mongodb.MongoSocketReadTimeoutException: Timeout while receiving message
    • 即出现了超时和maxWaitQueueSize超出
  2. 参数
    • connectionsPerHost
    • threadsAllowedToBlockForConnectionMultiplier
    • 原因是登录的时候是多线程执行的(如上百线程),第一次登录通常要访问数据库,即会出现上百线程同时访问数据库,从而数据库连接池这块出现问题
  3. 实践思路1
    • connectionsPerHost这个思路通常是和数据库服务器挂钩的
    • 如数据库服务器的瓶颈或者最佳性能点是允许的最大连接数是96
    • 而我们如果有12个进程同时访问这个数据库的话,那么简单来说一个进程允许的连接数就是8
  4. 实践思路2
    • threadsAllowedToBlockForConnectionMultiplier
    • 回到这个参数,而这个参数通常是和业务这边线程数目挂钩的,因为通常来说业务的线程数目是不定的,如果业务线程都要访问数据库,那么则需要设置这个参数保证当线程无法获取连接的时候都阻塞,而不是抛出异常(connectionsPerHost * threadsAllowedToBlockForConnectionMultiplier)
  5. 问题2:关于定时回写的多线程处理
    • 目前存储方案是定时回写,当目前是单线程处理回写
    • 之前讨论过一个方案是要多线程回写
    • 涉及到线程数目是多少,并非线程数目越多越好,需要和如connectionsPerHost等参数一些调来达到最佳的一个效果
  6. 问题3:mongo授权机制
    • 用authDbName去授权 username/password是在这个授权库下的用户
    • 否则每次新建库(如每一个游戏服务器都有自己单独的库) 都要手动授权 比较麻烦
  7. 问题4: spring-data-mongodb和mongo-java-driver版本要一致
  8. 问题5: 可使用Bulk Operations批量提升性能
  9. 问题6: mongodb服务器禁用透明大页

当前的一个存储策略

  1. 获取数据时先从缓存获取
  2. 如果缓存没有则从db加载,并放到缓存,并设置缓存过期时间和上次活跃时间(表示数据活跃)
  3. 在线玩家永不过期(即使不活跃)
  4. 每5分钟扫描一次缓存
    • 遍历缓存所有数据

    • 5分钟活跃的玩家都存储

    • 从缓存淘汰过期数据

      // 1. 5分钟内活跃的都进行存储 这里默认savePeriod就是5
      // 2. 第二个条件判断如果序列化时间较长 会出现有玩家在遍历过程中一直在修改活跃时间 当到了下一个5分钟的时候需要检查是否已经存过
      // 3. 会出现有的玩家在03存储的 05的时候又存储了一次 即这个5分钟是相对所以玩家
      // 4. 即最多出现5分钟回档
      if (data.getLastActiveTime() > now - this.savePeriod
                          && data.getLastActiveTime() > data.getLastSaveTime()) {
             this.persistenceData(data);
      }
      

参考链接

  1. https://www.jianshu.com/p/a8f653fc0c54
  2. https://juejin.im/post/5ad44c9b6fb9a028d70112e0
  3. http://www.importnew.com/11469.html
  4. http://www.importnew.com/12342.html
Clone this wiki locally