湖南禹班建设集团有限公司网站,外链图片,深圳市核酸检测点查询,电子书网站模板mongodb的特点#xff0c;为什么使用他
nosql数据库#xff0c;前端到后端到数据库#xff0c;都是json#xff0c;无模式#xff0c;数据模型发生变更#xff0c;不需要强制更新表结构#xff0c;可以快速实现需求迭代。
天生分布式#xff0c;高可用#xff0c;处…mongodb的特点为什么使用他
nosql数据库前端到后端到数据库都是json无模式数据模型发生变更不需要强制更新表结构可以快速实现需求迭代。
天生分布式高可用处理海量高并发的数据应用。
除了CRUD还有aggregation可以做分析报表有gridfs做分布式文件存储
各种致敬索引、慢查询、explain等无缝迁移
地理空间索引适合移动端业务
工具多mtools搭建集群、mongostat、mongotop等监控、mongoshake迁移
分布式高可用
涉及到 选举、复制、故障转移、数据均衡、复制延迟等
选举
需要强一致性一般都是采用raft算法的实现比如redis的选举、zk的选举等
流程
各种原因触发选举startElectSelfIfEligible -- 预选举dryRun -- 选举realElection -- 投自己票voteForMyself -- 处理投票结果onVoteRequestComplete -- 处理选举获胜的结果processWinElection -- 状态变更restartHeartbeats_inlock -- 进入追赶模式catchupState -- 判断是否追上abort_inlock -- 收尾模式drainComplete
根据什么确认可以当leader 心跳(其他节点可能率先完成选主)、任期(term)、opLog时间戳
为啥多了个预选举防止 网络异常导致的 分区下自己投自己选了很多次term增加很多然后网络恢复还要多一次选举的情况。
其他优化
多了个chainingAllowed 链式复制节点同步数据可以选距离自己最近的节点复制数据怎么判断最近心跳延时最小支持投票优先级氪金玩家体验更好选举策略可以PSS PSA也就是arbiter等脑裂的避免primary 在选举超时时间内没收到大多数节点的应答会自动退位成secondary
复制
采用opLog同步数据这里的oplog是一个特殊的固定集合当主节点上的一个写操作完成后会向oplog集合写入一条对应的日志而备节点则通过这个oplog不断拉取到新的日志在本地进行回放以达到数据同步的目的。由于日志会不断增加因此oplog被设计为固定大小的集合它本身就是一个特殊的固定集合capped collection当oplog的容量达到上限时旧的日志会被滚动删除
怎么实现的
一个环状的队列新文档在写入时会被插入队列的末尾如果队列已满那么之前的文档就会被新写入的文档所覆盖
类似disraptor
备节点便可以通过轮询的方式进行拉取这里会用到可持续追踪的游标tailable cursor技术 其实类似kafka的LEO同步
每个备节点都分别维护了自己的一个offset也就是从主节点拉取的最后一条日志的optime在执行同步时就通过这个optime向主节点的oplog集合发起查询。
每一条oplog记录都描述了一次数据的原子性变更对于oplog来说必须保证是幂等性的 如何实现 执行$inc操作每次都会产生新的结果。这些非幂等的更新命令在oplog中通常会被转换为$set操作
可能问题 如果备节点的复制不够快就无法跟上主节点的步伐从而产生复制延迟replication lag问题。
如何解决的
调整 writeConcern 和 readConcern 设置。适当降低 writeConcern 的级别可以减少写入时的延迟但要权衡数据安全性。启用压缩如启用 snappy 或 zlib 压缩来减少网络传输的数据量。优化 oplog 大小确保其足够大以容纳高峰期的写操作。
在开始时备节点仍然需要向主节点获得一份全量的数据用于建立基本快照这个过程就称为初始化同步initial sync
如何实现
● 备节点记录当前的同步optimet1来自主节点的同步时间戳进入STARTUP2状态。 ● 从主节点上复制所有非local数据库的集合数据同时创建这些集合上的索引。 在这个过程中备节点会开启另外一个线程将集合复制过程中的增量oplogt1之后产生也复制到本地。 ● 将拉取到t1之后的增量oplog进行回放在完成之前节点一直处于RECOVERING状态此时是不可读的。 ● oplog回放结束后恢复SECONDARY状态进入正常的增量同步流程。
故障转移
自动选举然后同步
对于业务方副本集发生主备切换的情况下会出现短暂的无主节点无法接受业务写操作
数据均衡
涉及到一个知识点chunk数据块
每个分片的最小结构是chunkchunk描述的是范围区间集群在操作分片集合时会根据分片键找到对应的chunk并向该chunk所在的分片发起操作请求 chunk的切分方式决定如何找到数据所在的chunk chunk的分布状态决定如何找到chunk所在的分片
分片策略有两种
范围分片 range sharding 可以选多个字段组合成分片键哈希分片 只能单个字段
分片策略选择因素
分片键的基数cardinality取值基数越大越有利于扩展。分片键的取值分布应该尽可能均匀。业务读写模式尽可能分散写压力而读操作尽可能来自一个或少量的分片。分片键应该能适应大部分的业务操作。
避免广播查询根据分片键无法满足业务查询需求导致对所有分片做广播操作。
数据均衡的意思
所有数据应该均匀的分布在不同的chunk上 由分片策略决定每个分片的chunk数量尽可能的相近
如何保证每个分片的chunk数量尽可能的相近 呢
可以手动均衡 适用于hash分片初始的时候预分配一定数量的chunk另一种做法通过splitAt、moveChunk命令手动切分、迁移
可以自动均衡 开启自动均衡功能 setBalancerState(true) balancer发现了不均衡状态就会自动进行chunk的搬迁以达到均衡
chunk的分裂
默认一个chunk 64MB由chunkSize参数指定如果数据量超了会自动进行分裂将chunk切分为大小相同的两块。
chunk分裂基于分片键分片键的基数cardinality太小会导致无法分裂出现jumbo chunk如性别
写压力过大也可能导致分裂失败当chunk 的文档数超过1.3xavgObjectSize导致无法迁移
自动均衡
balancer位于Primary Config Server该节点同时控制chunk数据的搬迁流程。
具体迁移流程
分片shard0在持续的业务写入压力下产生了chunk分裂分片服务器通知Config Server进行元数据更新Config Server的自动均衡器对chunk分布进行检查发现shard0和shard1的chunk数差异达到了阈值向shard0下发moveChunk命令以执行chunk迁移。shard0执行指令将指定数据块复制到shard1。该阶段会完成索引、chunk数据的复制而且在整个过程中业务侧对数据的操作仍然会指向shard0所以在第一轮复制完毕之后目标shard1会向shard0确认是否还存在增量更新的数据如果存在则继续复制shard0完成迁移后发送通知此时Config Server开始更新元数据库将chunk的位置更新为目标shard1。在更新完元数据库后并确保没有关联cursor的情况下shard0会删除被迁移的chunk副本Config Server通知mongos服务器更新路由表。此时新的业务请求将被路由到shard1
迁移的阈值
不均衡状态的判断
chunk个数差异 20, 阈值2chunk个数差异20-79, 阈值4chunk个数差异80, 阈值8
数据均衡影响性能的解决方案
容易带来磁盘I/O使用率飙升或业务时延陡增等一个是使用SSD将数据均衡的窗口对齐到业务的低峰期以降低影响
config数据库更新配置 setActiveWindow start stop时间为凌晨
创建索引失败MongoError: too many namespaces/collections
涉及到存储引擎
mongodb我们用的3.0存储引擎使用的MMAPv1采用内存映射文件管理数据缺点是锁粒度是集合级别并发性能受影响缺少数据压缩磁盘利用率低
2015年3.2版本开始wiredTiger称为默认存储引擎 storage.mmapv1.nsSize mongodb mmapv1 存储引擎的 namespace size 有大小限制默认 16M大概 24,000 表和索引在一个 db 里 线上的 库的一个 db有 5000 左右个 collection每个表了 里差不多有 1~10 个索引 namespace啥意思就是mongo的collection
mongodb的OOM
背景
线上平台出现mongodb的oom高可用架构重新选举之后选举完又OOMmong重启达到分钟级别多个节点被OOM后不能很快拉起服务对业务产生很大影响。
分析
表面看有几个问题1是128个G的内存还会OOM肯定有优化空间2是为啥启动这么慢
通过表分析有很多大表其中一个巨大的表占用了110G且有频繁的读写原因是有很多冷数据未做冷热分离
优化
删除部分历史遗留数据如有些bak表mongodb的优化mongodb3.2以后采用wiredTiger引擎虽然不是内存数据库但是为了提高读写效率会最大化利用内存。修改evict的配置 db.adminCommand({setParameter: 1, wiredTigerEngineRuntimeConfig: eviction(threads_min1,threads_max8)}) 原先最小线程是4改为1减少不必要的线程开销减少IO抖动
深挖
mongo的内存使用
理想情况mongodb可以提供近似内存的读写性能wiredTiger有两级缓存第一层是操作系统的页面缓存第二层则是引擎提供的内部缓存 ![[Pasted image 20240713071010.png]]
数据读取的流程
数据库发起buffer IO读操作由操作系统将磁盘数据页加载到文件系统的页缓存区。引擎层读取页缓存区的数据进行解压后存放到内部缓存区。在内存中完成匹配查询将结果返回给应用
如果数据已经被存储在内部缓存中MongoDB则可以发挥最佳的读性能。稍差的情况是内部缓存中找不到但数据仍然被存储在操作系统的页缓存中此时需要花费一些数据解压缩的开销。为了尽可能保证业务查询的“热数据”能快速被访问其内部缓存的默认大小达到了内存的一半对应参数wiredTigerCacheSize指定的。
数据写入的流程 先在内存中记录这些变更之后通过CheckPoint机制将变化的数据写入磁盘
带来问题可靠性
解决方案
checkpoint检查点机制类似RDB建立checkpoint的时候会在内存建立所有数据的一致性快照是通过MVCC保证然后持久化快照默认1min一次成功后内存中的修改才会真正保存。
journal日志 WAL机制预写顺序写会将每个写操作的redo日志写入journal缓冲区频繁地将日志持久化到磁盘上。一般100ms一次。如果journal日志达到100MB或者应用程序指定journal为true也会触发。
本质存量(快照)增量(journal)
实际写入的完整流程
应用写数据CUDmongo从内部缓存获取当前记录的page如果不存在从磁盘加载 buffer IOwiredTiger开始执行写事务修改的数据写入page的一个更新记录表原来的记录保持不变如果开启journal日志写入的同时会写journal日志也就是redo log不超过100ms将日志写磁盘mongo每60s执行一次checkpoint将内存的修改真正刷盘
然后就是脏页 dirty page
需要说到缓存页的管理 page管理也是B树当叶子节点产生数据写入更新记录会写入节点的一块独立区域此时该节点被标记为脏页。其中insert和update是单独的跳表分别存插入和修改操作如果存在修改读取的时候会从跳表做合并查找。
checkpoint的时候block manager发起reconncilication过程将内存页转换为磁盘页的格式checkpoint线程会遍历内存中全部页并找到所有脏页进行持久化一般用copy-onwrite保证读写分离。
对于脏页不是就地更新而是产生新节点每次都产生一个新的根节点持久化完成再淘汰不用的节点。
reconciliation的触发
checkpoint缓存的page超过最大值(存在大量修改)产生分裂触发evict缓存的脏数据比例达到阈值触发缓存淘汰evict
缓存淘汰
wiredTiger基于LRU实现缓存的淘汰通常由后台evict线程负责如果内存很紧张用户线程也会加入读写卡顿。
淘汰策略 ![[Pasted image 20240713074054.png]]
从官网上看的 数据压缩
集合采用块压缩默认采用谷歌开源的snappy索引用前缀压缩 prefix compressionjournal日志也是snappy压缩 压缩算法可以调整storage.wiredTiger.collectionConfig.blockCompressor mongodb4.2开始支持Zstdfacebook开源的较低的CPU消耗实现更高压缩比
用内存做什么
mongo的数据读写mongo连接线程管理操作如创建索引、数据备份
内部缓存增大后内存中允许驻留的脏数据也会更多导致磁盘IO抖动问题更加明显
mongodb cursor not found
背景
使用云厂商提供的mongodb分片集群client–slb–mongos数据量大的时候报错[AllExceptionsFilter] CaughtException: MongoError: Cursor not found (namespace: v7common.users, id: 3392892230983559305).
分析
游标失效了通过slb请求mongos策略的原因每次查的mongos不一样游标在一个mongos打开后续请求路由到另一个mongos了导致游标丢失。
优化
SLB采用会话保持让每个客户端的会话被路由到自己对应的mongos实例
mongo的cursor
从应用层面看游标类似一个指针看mongodriver是个迭代器对于find结果进行遍历其实是通过MongoCursor对象操作。真实的实现做了优化每次获取一批数据放到内存。具体细节
第一次提交查询才会携带查询条件、排序分页等参数如果一次查询不完通过getMore操作用cursorId进行分批拉取调用next方法其实是获取缓存的一条数据当缓存遍历完毕自动获取下一批每次拉取的条数由batchSize参数决定如果没有batchSize默认首次find返回最多101条数据后续getMore没有限制的化默认返回不超过16MB的数据
游标有个超时时间默认10min那不应该cursor not found的啊
需要了解mongos的原理
mongos查询路由是分片集群的访问入口从config server获取元数据并加载然后提供访问服务将用户请求路由到对应分片。
从不同的slb过来的被认为不同的连接导致游标失败
连接池偶发断开
背景
华为云ELBmongo连接池偶发断开
![[Pasted image 20240713102322.png]] 一段时间后大概一分钟还能自动恢复 ![[Pasted image 20240713102337.png]]
2021-11-16 12:46:11.938 nacos [http-nio-8089-exec-4] WARN org.mongodb.driver.connection - Got socket exception on connection [connectionId{localValue:95, serverValue:9552231}] to 172.16.3.182:7211. All connections to 172.16.3.182:7211 will be closed.
2021-11-16 12:46:11.939 nacos [http-nio-8089-exec-4] INFO org.mongodb.driver.connection - Closed connection [connectionId{localValue:95, serverValue:9552231}] to 172.16.3.182:7211 because there was a socket exception raised by this connection.
2021-11-16 12:46:11.939 nacos [http-nio-8089-exec-4] INFO org.mongodb.driver.cluster - No server chosen by ReadPreferenceServerSelector{readPreferenceprimary} from cluster description ClusterDescription{typeSHARDED, connectionModeMULTIPLE, serverDescriptions[ServerDescription{address172.16.3.182:7211, typeUNKNOWN, stateCONNECTING}]}. Waiting for 30000 ms before timing out
2021-11-16 12:46:11.940 nacos [http-nio-8089-exec-4] INFO org.mongodb.driver.connection - Closed connection [connectionId{localValue:94, serverValue:9552230}] to 172.16.3.182:7211 because there was a socket exception raised on another connection from this pool.![[Pasted image 20240713102409.png]]
mongo日志也有报错
![[Pasted image 20240713102424.png]]
分析
ELB导致的mongos的问题云不是真正的高可靠打破大厂迷信。
mongo的常用工具
mongostat
/opt/mongodb3.2.13/bin/mongostat -h 10.152.206.81 --port 7210 -u xxx -p xxx --authenticationDatabaseadmin --discover
![[Pasted image 20240713070357.png]] 一般用来查看QPS、内存占用、连接数等里面vsize是虚拟内存 res是物理内存使用量
另外就是
CRUD的速率是否有波动超出预期connect 连接数是否太多dirty 百分比是否较高如果持续高于10%说明磁盘IO存在瓶颈repl 状态是否异常如果 RTR正常如果REC等异常值需要修复
mongotop 用来查看热点表
是否存在非预期的热点表其实就是看是否有慢查询热点表的操作耗时是否过高业务高峰一般比较高一点
可以设置 -n 100 2 就是间隔2s总共输出100次
迁移
采用 mongoshake
mongodb相关优化
mongo查询数据以后应该使用project只返回需要使用的字段否则在数据量、字段比较多的时候查询效率会显著下降
索引覆盖索引