
# 排查DDS实例内存占用较高的问题
使用文档数据库服务时，如果您的内存使用率很高或接近100%，不仅会导致业务请求响应缓慢，也会增大OOM风险，从而影响业务稳定运行。
本章节帮助您分析数据库连接数、慢请求和游标，经过分析优化后，使得数据库的查询相对合理，所有的请求都高效使用了索引，并规范使用游标，从而排查文档数据库服务内存使用率高的问题。如果经确认确实为业务增长导致的内存升高，建议及时进行[规格扩容](https://support.huaweicloud.com/usermanual-dds/zh-cn_topic_0104472218.html)。
#### 排查连接数
1. 查看[连接数占比](https://support.huaweicloud.com/usermanual-dds/dds_03_0076.html)，总的连接数不宜超过当前实例能承受的最大连接数的80%。连接太多会导致内存和多线程上下文的开销增加，影响请求处理延时。
2. 建议配置连接池，一般情况建议连接池最大不要超过200，具体操作可参考[查询及限制连接数](https://support.huaweicloud.com/dds_faq/dds_faq_0040.html)。
 
#### 分析慢日志
除了降低连接数以外，还需要注意单次请求的内存开销，尽量避免查询语句出现全表扫描、内存排序等。
1. 使用[慢日志功能](https://support.huaweicloud.com/usermanual-dds/zh-cn_topic_slow_query_log.html)，查询当前实例产生的慢日志。
2. 分析慢日志，查找内存升高的原因：下面是某个慢请求日志示例，可查看到该请求进行了全表扫描，扫描了1561632个文档，没有通过索引进行查询。
   ```
   {
           "op" : "query",
           "ns" : "taiyiDatabase.taiyiTables$10002e",
           "query" : {
                   "find" : "taiyiTables",
                   "filter" : {
                           "filed19" : NumberLong("852605039766")
                   },
                   "shardVersion" : [
                           Timestamp(1, 1048673),
                           ObjectId("5da43185267ad9c374a72fd5")
                   ],
                   "chunkId" : "10002e"
           },
           "keysExamined" : 0,
           "docsExamined" : 1561632,
           "cursorExhausted" : true,
           "numYield" : 12335,
           "locks" : {
                   "Global" : {
                           "acquireCount" : {
                                   "r" : NumberLong(24672)
                           }
                   },
                   "Database" : {
                           "acquireCount" : {
                                   "r" : NumberLong(12336)
                           }
                   },
                   "Collection" : {
                           "acquireCount" : {
                                   "r" : NumberLong(12336)
                           }
                   }
           },
           "nreturned" : 0,
           "responseLength" : 157,
           "protocol" : "op_command",
           "millis" : 44480,
           "planSummary" : "COLLSCAN",
           "execStats" : {
                 "stage" : "SHARDING_FILTER",                                                                                                                                       [3/1955]
                   "nReturned" : 0,
                   "executionTimeMillisEstimate" : 43701,
                   "works" : 1561634,
                   "advanced" : 0,
                   "needTime" : 1561633,
                   "needYield" : 0,
                   "saveState" : 12335,
                   "restoreState" : 12335,
                   "isEOF" : 1,
                   "invalidates" : 0,
                   "chunkSkips" : 0,
                   "inputStage" : {
                           "stage" : "COLLSCAN",
                           "filter" : {
                                   "filed19" : {
                                           "$eq" : NumberLong("852605039766")
                                   }
                           },
                           "nReturned" : 0,
                           "executionTimeMillisEstimate" : 43590,
                           "works" : 1561634,
                           "advanced" : 0,
                           "needTime" : 1561633,
                           "needYield" : 0,
                           "saveState" : 12335,
                           "restoreState" : 12335,
                           "isEOF" : 1,
                           "invalidates" : 0,
                           "direction" : "forward",
                           "docsExamined" : 1561632
                   }
           },
           "ts" : ISODate("2019-10-14T10:49:52.780Z"),
           "client" : "172.16.36.87",
           "appName" : "MongoDB Shell",
           "allUsers" : [
                   {
                           "user" : "__system",
                           "db" : "local"
                   }
           ],
          "user" : "__system@local"
   }
   ```
   在慢请求日志中，您需要重点关注以下关键字。
   - 全集合（全表）扫描：COLLSCAN
     - 当一个操作请求（如query、update、delete）需要全表扫描时，将大量占用内存资源。在查看慢请求日志时，发现COLLSCAN关键字，很可能是这些查询占用了内存资源。
     
     - 如果该类操作请求较为频繁，建议您对查询的字段建立索引进行优化。
      
   
   
   
   - 全集合（全表）扫描：docsExamined
     - 通过查看参数"docsExamined"的值，可以查看一个查询扫描了多少文档。该值越大，请求的内存使用率越高。
      
   
   
   
   - 不合理的索引：IXSCAN、keysExamined
   
   - 大量数据排序：SORT、hasSortStage
    
   当查询请求中包含排序时，参数"hasSortStage"的值为"true"。如果排序无法通过索引实现，将在查询结果中进行排序。由于排序将占用大量内存资源，该场景下，需要通过对经常排序的字段建立索引进行优化。
   当您发现SORT关键字时，可以考虑通过索引来优化排序。
   ![](https://support.huaweicloud.com/bestpractice-dds/public_sys-resources/note_3.0-zh-cn.png)
   索引不是越多越好，过多索引会影响写入和更新的性能。越建议参考ESR原则设计索引，以提高查询效率：
   - 精确（Equal）匹配的字段放最前面。
   
   - 排序（Sort）条件放中间。
   
   - 范围（Range）匹配的字段放最后面。
     
 
#### 检查游标
游标不规范的使用很容易造成内存升高并且长期不释放的情况，当客户端使用数据库的游标功能时，一定注意主动释放游标（游标的[官方说明](https://www.mongodb.com/docs/manual/reference/command/find/)）。
1. 检查游标是否有被设置为不超时，默认情况下数据库会在10分钟后自动释放游标。Java driver给出的游标超时示例代码如下：
   ```
   MongoCursor<Document> cursor = collection.find(query)
                       .maxTime(10, TimeUnit.MINUTES)
                       .iterator();
   ```
   
2. 在使用完游标后客户端是否有主动释放游标。Java driver中的释放示例如下：
   ```
   cursor.close()
   ```
   
3. 如果已有noTimeout（不超时）游标，并且客户端已经无法释放，可[重启内存高的实例节点](https://support.huaweicloud.com/usermanual-dds/dds_03_0003.html)以释放这些游标，但需要注意后续优化业务代码，尽量避免设置不超时游标，并在使用完游标后主动释放。
 
