
# 开发规范
#### 数据库连接
使用DDS时，可能会遇到因为Mongod/dds mongos的连接数满了，导致客户端无法连接的问题。在Mongod/dds mongos的服务端，收到一个新的连接由一个单独的线程来处理，每个线程配置了1MB的栈空间，当网络连接数太多时，过多的线程会导致上下文切换开销变大，同时内存开销也会上涨。
- 客户端连接数据库的时候，要计算业务一共有多少个客户端，每个客户端配置的连接池大小是多少，总的连接数不要超过当前实例能承受的最大连接数的80%。
- 客户端与数据库的连接应尽量保持相对稳定的状态，每秒新增连接数建议保持在10以下。
- 建议客户端的连接超时时间至少设置为最大业务执行时长的3倍。
- 对于副本集实例，客户端需要同时配置主备节点的IP地址；对于集群实例，至少配置两个dds mongos的IP地址。
- DDS默认提供rwuser用户，使用rwuser用户登录时认证库必须是admin。
 
#### 可靠性
write concern设置规则：对于关键业务，write concern设置为{w:n},n\>0，数字越大，一致性实现更好，但性能较差。
- w:1表示实际写入主节点完成返回。
- w:1,journal:true表示写主节点和日志后返回。
- w:majority表示大多数备节点写入后返回。
  ![](https://support.huaweicloud.com/usermanual-dds/public_sys-resources/note_3.0-zh-cn.png)
  如果没有以w:majority写入数据，则发生主备倒换时，未同步到备机的数据有丢失风险。
  
 
对于可靠性有较高要求的，建议采用3AZ部署的集群。
#### 性能相关
**规范**
- 业务程序禁止执行全表扫描的查询。
- 执行查询时，只选择需要返回的字段，不需要的字段不要返回。从而减少网络和进程处理的负载，修改数据时，只修改变化需要修改的字段，不要整个对象直接存储全部修改。
- 避免使用$not。DDS并不会对缺失的数据进行索引，因此$not的查询条件将会要求在一个结果集中扫描所有记录。如果$not是唯一的查询条件，会对集合执行全表扫描。
- 用$and时把匹配最少结果的条件放在最前面，用$or时把匹配最多结果的条件放在最前面。
- 单个实例中，数据库的总的个数不要超过200个，总的集合个数不要超过500个。集合数量过多会导致内存压力变高，并且集合数量多会导致重启以及主备倒换性能变差，影响紧急情况下的高可用性能。
- 业务上线前，一定要对数据库进行性能压测，评估业务峰值场景下，对数据库的负载情况。
- 禁止同时执行大量并发事务，且长时间不提交。
- 业务正式上线前， 所有的查询类别，都应该先执行查询计划检查查询性能。
 
**建议**
- 每个连接在后台都是由一个单独线程处理，每个线程会分配1MB的栈内存。所以连接数不宜过多，否则会占用过多的内存。
- 使用连接池，避免频繁地建立连接和断开连接，否则会导致CPU过高。
- 减少磁盘读写：避免使用不必要的upsert命令，避免查询不必要的数据。
- 优化数据分布：对数据进行分片，同时分散热点数据，均衡地使用实例资源。如何进行数据分片，请参见[设置数据分片](https://support.huaweicloud.com/bestpractice-dds/dds_0005.html)。
- 减少锁冲突：避免对同一个Key过于频繁地操作。
- 减少锁等待：避免前台创建索引。
 
**注意**
开发过程中对集合的每一个操作都要通过执行explain()检查其执行计划，如：
**db.T_DeviceData.find({"deviceId":"ae4b5769-896f"}).explain();**
**db.T_DeviceData.find({"deviceId":"77557c2-31b4"}).explain("executionStats");**
对于查询而言，因为覆盖查询不需要读取文档，而是直接从索引中返回结果，这样的查询性能好，所以**尽可能使用索引覆盖查询**。如果explain()的输出显示indexOnly字段为真，则说明这个查询就被一个索引覆盖。
执行计划解析：
1. 看执行时间：executionStats.executionStages.executionTimeMillisEstimate和executionStats.executionStages.inputStage. executionTimeMillisEstimate时间越短越好。
   - executionStats.executionTimeMillis表示执行计划选择和执行的所有时间。
   
   - executionStats.executionStages.executionTimeMillisEstimate表示执行计划的执行完成时间。
   
   - executionStats.executionStages.inputStage. executionTimeMillisEstimate表示执行计划下的子阶段执行完成时间。
    
2. 看扫描条数：三个条目数相同为最佳。
   - executionStats. nReturned表示匹配查询条件的文档数。
   
   - executionStats .totalKeysExamined表示索引扫描条目数。
   
   - executionStats .totalDocsExamined表示文档扫描条目数。
    
3. 看Stage状态，性能较好的Stage状态组合如下。
   - Fetch+IDHACK
   
   - Fetch+ixscan
   
   - Limit+（Fetch+ixscan）
   
   - PROJECTION+ixscan
   
   
   表1状态说明 
   | **状态名称**   | **描述**          |
   |:---|:---|
   | COLLSCAN   | 全表扫描            |
   | SORT       | 内存中进行排序         |
   | IDHACK     | 根据_id进行查询       |
   | TEXT       | 全文索引            |
   | COUNTSCAN  | 未用索引计数          |
   | FETCH      | 索引扫描            |
   | LIMIT      | 使用Limit限制返回数    |
   | SUBPLA     | 未用索引的$or查询阶段    |
   | PROJECTION | 限定返回字段时stage的返回 |
   | COUNT_SCAN | 使用索引计数          |
      
   
 
#### Cursor使用规则
如果cursor不使用了要立即关闭。由于cursor在10分钟内不活动，就会关闭，立即手动关闭会节省资源。
#### 4.2版本分布式事务使用规则
- Spring Data MongoDB不支持事务报错后重试机制，如果客户端使用Spring Data Mongo作为连接MongoDB的客户端，需要依照Spring Data Mongo的参考文档，使用Spring Retry进行事务的重试操作。

- 分布式事务操作数据的大小不能超过16MB。
 
#### 备份相关注意事项
备份期间应避免进行DDL操作，规避备份失败的风险。
