Mongodb
mongodb数据格式是BSON(Binary JSON),一种类似JSON的二进制(字节流)形式的存储格式。
BJson在空间占用上相比Json没有明显优势,但在解析效率上比json好。
MongoDB中,一个BSON文档最大大小为16M,文档嵌套的级别不超过100。
MongoDB集合中所有的文档都有一个唯一的_id字段,作为集合的主键。在默认情况下,_id字段使用ObjectId类型,采用16进制编码形式,共12个字节。
MongoDB基于灵活的JSON文档模型,非常适合敏捷式的快速开发。与此同时,其与生俱来的高可用、高水平扩展能力使得它在处理海量、高并发的数据应用时颇具优势。
1.高可用
支持复制(副本)集
2.横向扩展能力(大数据量)
支持分片,需要设置分片策略
文档操作最佳实践
1.关于文档结构
防止使用太长的字段名(浪费空间)
防止使用太深的数组嵌套(超过2层操作比较复杂)
不使用中文,标点符号等非拉丁字母作为字段名
数据库名和集合名称均不能超过64个字符。数据库名使用小写字符。
2.关于写操作
update 语句里只包括需要更新的字段
尽可能使用批量插入来提升写入性能
使用TTL自动过期日志类型的数据
3.尽量不要复杂join,即lookup
4.数据量大的时候,应该避免使用skip/limit形式的分页。
(如果没有唯一排序条件,即没有一个字段是排好序的,那么还是用skip/limit吧)
替代方案:使用查询条件+唯一排序条件;
例如:
第一页:db.posts.find({}).sort({_id: 1}).limit(20);
第二页:db.posts.find({_id: {KaTeX parse error: Expected 'EOF', got '}' at position 17: …t: <第一页最后一个_id>}̲}).sort({_id: 1…gt: <第二页最后一个_id>}}).sort({_id: 1}).limit(20);
5.处理分页问题 – 避免使用 count(所以对于数据量大的,如果要使用count,需要建立索引)
尽可能不要计算总页数,特别是数据量大和查询条件不能完整命中索引时。
考虑以下场景:假设集合总共有 1000w 条数据,在没有索引的情况下考虑以下查询:
1 db.coll.find({x: 100}).limit(50);
2 db.coll.count({x: 100});
前者只需要遍历前 n 条,直到找到 50 条 x=100 的文档即可结束;
后者需要遍历完 1000w 条找到所有符合要求的文档才能得到结果。 为了计算总页数而进行的 count() 往往是拖慢页面整体加载速度的原因
6.单个分片的数据集合大小建议不超过2TB。
最好在集合达到256GB之前就进行分片。
7.优先考虑为系统日志、历史数据表添加合理的老化策略。(如TTL索引、定期清理、自动清理功能、数据归档)
8.数据一致性方面,非关键业务使用默认的WriteConcern:1(更高性能写入);对于关键业务类,使用WriteConcern:majority保证一致性(性能下降)。如果业务上严格不允许脏读,则使用ReadConcern:majority选项
9.业务上尽量避免短连接,使用官方最新驱动的连接池实现,控制客户端连接池的大小,最大值建议不超过200。
10.优先使用单文档事务保证原子性,如果需要使用多文档事务,则必须保证事务尽可能小,一个事务的执行时间最长不能超过60s。
11.在条件允许的情况下,利用读写分离降低主节点压力。(即使用复制集)
WiredTiger读写模型
(主要讲WiredTiger读写模型优点及写缓冲下如何保证数据可靠性/不丢失)
MongoDB从3.0开始引入可插拔存储引擎的概念。目前主要有MMAPV1、WiredTiger存储引擎可供选择。
WiredTiger读写模型优点:
WiredTiger读写模型通过多线程并发操作、数据压缩、高效的缓存管理、原子性操作和事务支持等功能,优化了MongoDB数据库的读写性能和并发性能,提高数据库的吞吐量和可靠性,节省了磁盘资源。使其能够更好地应对大规模数据处理和高并发访问的需求。
WiredTiger读写模型-读缓存
1.理想情况下,MongoDB可以提供近似内存式的读写性能。
2.WiredTiger引擎实现了数据的二级缓存,第一层是操作系统的页面缓存,第二层则是引擎提供的内部缓存。
3.读取数据时的流程如下:
数据库发起Buffer I/O读操作,由操作系统将磁盘数据页加载到文件系统的页缓存区。
引擎层读取页缓存区的数据,进行解压后存放到内部缓存区。
在内存中完成匹配查询,将结果返回给应用。
4.MongoDB为了尽可能保证业务查询的“热数据”能快速被访问,其内部缓存的默认大小达到了内存的一半,该值由wiredTigerCacheSize参数指定,其默认的计算公式如下:
1 wiredTigerCacheSize=Math.max((RAM/2‐1GB),256MB)
关于CheckPoint:建立CheckPoint时,WiredTiger会在内存中建立所有数据的一致性快照,并将该快照覆盖的所有数据变化
一并进行持久化(fsync)。成功之后,内存中数据的修改才得以真正保存。默认情况下,MongoDB每60s建立一次CheckPoint,在检查点写入过程中,上一个检查点仍然是可用的。这样可以保证一旦出错,MongoDB仍然能恢复到上一个检查点。
WiredTiger读写模型-写缓冲
1.当数据发生写入时,MongoDB并不会立即持久化到磁盘上,而是先在内存中记录这些变更,之后通过CheckPoint机制将变化的数据写入磁盘。为什么要这么处理?主要有以下两个原因:
如果每次写入都触发一次磁盘I/O,那么开销太大,而且响应时延会比较大。
多个变更的写入可以尽可能进行I/O合并,降低资源负荷。
2.因为有缓存、MongoDB会丢数据吗?
(MongoDB单机下保证数据可靠性的机制包括以下两个部分(和mysql一样,使用了mvcc和
redo log保证使用缓存下数据一致性,不丢失))
a.CheckPoint(检查点)机制(mvcc 多版本并发控制)
默认情况下,MongoDB每60s建立一次CheckPoint。
CheckPoint的刷新周期可以调整storage.syncPeriodSecs参数(默认值60s),
在MongoDB 3.4及以下版本中,当Journal日志达到2GB时同样会触发CheckPoint行为。如果应用存在大量随机写入,则CheckPoint可能会造成磁盘I/O的抖动。在磁盘性能不足的情况下,问题会更加显著,此时适当缩短CheckPoint周期可以让写入平滑一些。
b.Journal日志(将redo日志写入到journal)
默认情况下,Journal缓冲区每100ms执行一次持久化。
Journal日志的刷新周期可以通过参数storage.journal.commitIntervalMs指定
MongoDB 3.4及以下版本的默认值是50ms,而3.6版本之后调整到了100ms。由于Journal日志采用的是顺序I/O写操作,频繁地写入对磁盘的影响并不是很大。

因此,Journal 日志和 Checkpoint 机制在数据库系统中是相辅相成的。Checkpoint 主要解决的是数据持久性和性能优化(减少写入操作的频率),而Journal 日志则解决的是数据库故障后的恢复问题。它们共同确保了数据库的可靠性和稳定性。
3.WiredTiger写入数据的流程:
应用向MongoDB写入数据(插入、修改或删除)。
数据库从内部缓存中获取当前记录所在的页块,如果不存在则会从磁盘中加载(Buffer I/O)
WiredTiger开始执行写事务,修改的数据写入页块的一个更新记录表,此时原来的记录仍然保持不变。
如果开启了Journal日志,则在写数据的同时会写入一条Journal日志(RedoLog)。该日志在最长不超过100ms之后写入磁盘。
数据库每隔60s执行一次CheckPoint操作,此时内存中的修改会真正刷入磁盘。
MongoDB索引
WiredTiger 是MongoDB的数据存储引擎
myisam和innodb是mysql的数据存储引擎
他们都使用了b+树作为索引的数据结构,都是基于磁盘的数据存储引擎。
WiredTiger数据文件在磁盘的存储结构
wiredTiger和mysql存储引擎的区别主要是体现在磁盘文件数据中的数据结构不同(红框
内)。innodb磁盘文件中数据的数据结构不是这样的。
索引设计原则
(每个集合最好都创建索引,但是更新多的集合尽量不要创建过多索引)
1、每个查询原则上都需要创建对应索引
2、单个索引设计应考虑满足尽量多的查询
3、索引字段选择及顺序需要考虑查询覆盖率及选择性
4、对于更新及其频繁的字段上创建索引需慎重
5、对于数组索引需要慎重考虑未来元素个数
6、对于超长字符串类型字段上慎用索引
7、并发更新较高的单个集合上不宜创建过多索引
没有创建主键索引的时候,默认会帮我们创建_id主键索引
mongodb 索引类型和索引属性的区别
在 MongoDB 中,索引类型和索引属性代表了索引的不同方面。它们之间的区别如下:
索引类型:
索引类型指的是索引的数据结构或组织方式,用于加快查询操作的速度。
索引属性:
索引属性指的是在创建索引时可以指定的一些属性或选项,用于调整索引的行为和特性。
MongoDB 的索引属性包括了多种设置选项,例如唯一性约束、稀疏索引、部分索引等。
通过设置不同的索引属性,可以使索引具有不同的特性,满足不同的查询需求。
db.users.createIndex(
{ username: 1 }, // 索引键为 username 字段,升序排序
{ unique: true, sparse: true } // 唯一性约束为 true,稀疏索引为 true
)
在这个示例中:
索引类型为单键索引。
索引属性包括了 unique: true 和 sparse: true。
unique: true 表示该索引的值必须是唯一的,不允许重复。
sparse: true 表示该索引只包含非空的字段值,如果某个文档的 username 字段不存在或为空,则该文档不会被包含在索引中。
这样,我们就同时指定了索引类型和索引属性,创建了一个唯一且稀疏的索引,以满足我们的需求。
索引类型
1.单键索引(Single Field Indexes)
2.复合索引(Compound Index)
复合索引是多个字段组合而成的索引
3.多键索引(Multikey Index)(用于数组上)
在数组的属性上建立索引
针对这个数组的任意值的查询都会定位到这个文档,既多个索引入口或者键值引用同一个文档
多键索引很容易与复合索引产生混淆,复合索引是多个字段的组合,而多键索引则仅仅是在
一个字段上出现了多键(multi key)。而实质上,多键索引也可以出现在复合字段上。
4.地理空间索引(Geospatial Index)
5.全文索引(Text Indexes)
6.Hash索引(Hashed Indexes)
7.通配符索引(Wildcard Indexes)
索引属性
1.唯一索引(Unique Indexes)
2.部分索引(Partial Indexes)
部分索引仅对满足指定过滤器表达式的文档进行索引。
唯一约束结合部分索引使用导致唯一约束失效的问题
注意:如果同时指定了partialFilterExpression和唯一约束,那么唯一约束只适用于满足筛选器表达式的文档。如果文档不满足筛选条件,那么带有惟一约束的部分索引不会阻止插入不满足惟一约束的文档。
3.稀疏索引(Sparse Indexes)
4.TTL索引(TTL Indexes)
并非所有的数据都需要永久存储。例如一些系统事件、用户消息等。
5.隐藏索引(Hidden Indexes)
隐藏索引对查询规划器不可见,不能用于支持查询。通过对规划器隐藏索引,用户可以在不
实际删除索引的情况下评估删除索引的潜在影响。如果影响是负面的,用户可以取消隐藏索
引,而不必重新创建已删除的索引。4.4新版功能。
mongodb也可以explain查看执行计划
聚合操作
聚合操作包含三类:单一作用聚合、聚合管道、MapReduce。
1.单一目的聚合操作
单一作用聚合是指在数据库中进行一次性聚合操作,而不需要使用聚合管道。这种方式通常包括使用数据库提供的内置函数或操作符来进行数据聚合,例如:
MongoDB提供db.collection.estimatedDocumentCount(), db.collection.count()和db.collection.distinct()。
所有这些操作都汇总了单个集合中的文档。尽管这些操作提供了对常见聚合过程的简单访问,但是它们缺乏聚合管道和映射减少的灵活性和功能。

db.collection.estimatedDocumentCount(), db.collection.count()的区别?
在 MongoDB 中,db.collection.estimatedDocumentCount() 和 db.collection.count() 是两个用于统计集合中文档数量的方法,它们之间有以下区别:
db.collection.estimatedDocumentCount():
a.这是 MongoDB 4.0 版本及以后推荐使用的方法。
b.它返回估计的集合中文档的数量,而不会扫描集合或者做精确的计数。
c.这意味着它能够快速返回一个近似的文档数量,而不会受到集合大小的影响。
db.collection.count():
a.在 MongoDB 4.0 之前,这是用于获取集合中文档数量的方法。
b.它会完全扫描集合,并返回准确的文档数。
c.由于要完全扫描集合,对于大型集合可能会比较耗时,并且可能会影响性能。
大多数情况下,建议使用 estimatedDocumentCount() 来获取集合的文档数量,除非需要确切的文档计数,这时可以考虑使用 count() 方法。
2.聚合管道
MongoDB的聚合框架以数据处理管道的概念为模型。文档进入多阶段流水线,该流水线将文档转换成汇总结果。例如:
在这个例子中
db.orders.aggregate([
{ $match: { status: “A” } },
{ KaTeX parse error: Expected '}', got 'EOF' at end of input: group: { _id: "cust_id", total: { sum:"sum: "sum:"amount" } } }
])
第一阶段:match阶段按status字段过滤文档,并将status等于的文档传递到下一阶段"A"。第二阶段:该match阶段按status字段过滤文档,并将status等于的文档传递到下一阶段"A"。
第二阶段:该match阶段按status字段过滤文档,并将status等于的文档传递到下一阶段"A"。第二阶段:该group阶段按cust_id字段将文档分组,以计算每个唯一值的总和cust_id。
常用的管道聚合阶段
聚合管道包含非常丰富的聚合阶段,下面是最常用的聚合阶段

3.MapReduce
MapReduce操作将大量的数据处理工作拆分成多个线程并行处理,然后将结果合并在一起。MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。
MapReduce具有两个阶段:
- 将具有相同Key的文档数据整合在一起的map阶段
- 组合map操作的结果进行统计输出的reduce阶段
MapReduce的基本语法


高可用方案(使用复制集架构)
在生产环境中,不建议使用单机版的MongoDB服务器。原因如下:
单机版的MongoDB无法保证可靠性,一旦进程发生故障或是服务器宕机,业务将直接不可
用。
一旦服务器上的磁盘损坏,数据会直接丢失,而此时并没有任何副本可用。
mongodb复制集群是主从结构,是主节点负责读写、从节点只负责读。
早期版本的MongoDB使用了一种Master-Slave的架构,该做法在MongoDB 3.4版本之后已经废弃。
复制集架构(三节点复制集模式)
1.PSS模式(官方推荐模式)
PSS模式由一个主节点和两个备节点所组成,即Primary+Secondary+Secondary。
2.PSA模式
PSA模式由一个主节点、一个备节点和一个仲裁者节点组成,即Primary+Secondary+Arbiter
仲裁节点不存储数据副本,也不提供业务的读写操作,只参与选举
典型三节点复制集环境搭建及使用
即使暂时只有一台服务器,也要以单节点模式启动复制集。即只有一台机器时,要指定该单节点进程是一个复制集,而不是只是单实例。启动复制集后,后面可以进行水平扩容。
1.单机多实例启动复制集(单机下启动多实例会占用空间)
2.单节点启动复制集(单机可使用这种方案)
复制集选举原理
MongoDB的复制集选举使用Raft算法(https://raft.github.io/)来实现,选举成功的必要条件是大多数投票节点存活。
在具体的实现中,MongoDB对raft协议添加了一些自己的扩展。
一个复制集最多可以有50 个成员,但只有 7 个投票成员。
自动故障转移原理
在故障转移场景中,我们所关心的问题是:
备节点是怎么感知到主节点已经发生故障的?
如何降低故障转移对业务产生的影响?
1.备节点是怎么感知到主节点已经发生故障的?(通过心跳)
通过心跳,在复制集组建完成之后,各成员节点会开启定时器,持续向其他成员发起心跳
但一次心跳检测失败并不会立即触发重新选举。如果备节点在一定时间内没有收到主节点的心跳响应,它会认为主节点可能已经发生故障,然后发起一次选举来选择新的主节点。
在electionTimeout任务中触发选举必须要满足以下条件:
(1)当前节点是备节点。
(2)当前节点具备选举权限。
(3)在检测周期内仍然没有与主节点心跳成功。
2.如何降低故障转移对业务产生的影响?
通过开启retrywrite启用可重试写入、或者在业务上设计重试机制
在复制集发生主备节点切换的情况下,会出现短暂的无主节点阶段,此时无法接受业务写操作。如果是因为主节点故障导致的切换,则对于该节点的所有读写操作都会产生超时。如果使用MongoDB 3.6及以上版本的驱动,则可以通过开启retryWrite来降低影响。
思考:如何优雅的重启复制集(包括主节点和从节点)?
优雅重启是为了确保在重启过程中尽可能减少对服务的影响,特别是在一个运行中的生产环境中。通过优雅重启,可以确保在重启过程中不会出现数据丢失或数据不一致的情况。
如果想不丢数据重启复制集,更优雅的打开方式应该是这样的:
- 逐个重启复制集里所有的Secondary节点
- 对Primary发送rs.stepDown()命令,等待primary降级为Secondary
- 重启降级后的Primary
复制集数据同步机制(oplog)
a.在复制集架构中,主节点与备节点之间是通过oplog来同步数据的。
b.什么是oplog?
1.MongoDB oplog 是 Local 库下的一个集合,用来保存写操作所产生的增量日志(类似于 MySQL 中 的 Binlog)。
2.它是一个 Capped Collection(固定集合),即超出配置的最大值后,会自动删除最老的历史数据,MongoDB 针对 oplog 的删除有特殊优化,以提升删除效率。
3.主节点产生新的 oplog Entry,从节点通过复制 oplog 并应用来保持和主节点的状态一致;
c.和redis主从数据复制一样,备节点/从节点会维护自己从主节点读取起始值的offset。
横向扩展方案,支持大数据量方案(使用分片集群架构)
一般不用
多文档事务
多文档事务既可以涉及操作同一集合中的多个文档,也可以涉及操作不同集合中的文档。
(springboot中mongodb本地事务和分布式事务用法和mysql一样,用@Transactional或@Seata)
在MongoDB中,对单个文档的操作是原子的。
对于需要多个文档(在单个或多个集合中)进行原子读写的场景,MongoDB 中引入了多文档事务和分布式事务。
多文档事务适用于单个MongoDB实例内的多个文档操作,确保这些操作要么全部成功提交,要么全部回滚。
分布式事务通常涉及多个数据源或多个数据库之间的操作,例如同时操作MongoDB和关系型数据库。在Spring Boot中,可以使用Spring的分布式事务管理器来实现跨多个资源的事务控制。
对于MongoDB而言,其默认的事务处理是非分布式的。
MongoDB 4.2 引入了分布式事务。
在4.2 中,分布式事务(Distributed Transactions)和多文档事务(Multi-Document Transactions)都是称为分布式事务(distributed transactions)。
使得分布到集群中不同sharded的多文档ACID事务变得更加简单。MongoDb通过二阶段提交的方式,实现在多个Shard间发生的修改,要么同时发生,要么都不发生,保证事务的ACID特性。
4.2版本具体查找资料
MongoDB 虽然已经在 4.2 开始全面支持了多文档事务,但并不代表大家应该毫无节制地使用它。相反,对事务的使用原则应该是:能不用尽量不用。 通过合理地设计文档模型,可以规避绝大部分使用事务的必要性。
writeConcern, readConcern
1.writeConcern 决定一个写操作落到多少个节点上才算成功。
在 readPreference 选择了指定的节点后,readConcern 决定这个节点上的数据哪些是可读的。
在读取数据的过程中我们需要关注以下两个问题:
从哪里读?
什么样的数据可以读?
第一个问题是是由 readPreference 来决定,第二个问题则是由 readConcern 来决定
readPreference决定使用哪一个节点来满足正在发起的读请求。可选值包括:
primary: 只选择主节点,默认模式;
primaryPreferred:优先选择主节点,如果主节点不可用则选择从节点;
secondary:只选择从节点;
secondaryPreferred:优先选择从节点, 如果从节点不可用则选择主节点;
nearest:根据客户端对节点的 Ping 值判断节点的远近,选择从最近的节点读
取。
合理的 ReadPreference 可以极大地扩展复制集的读性能,降低访问延迟。
在 readPreference 选择了指定的节点后,readConcern 决定这个节点上的数据哪些是可读的,类似于关系数据库的隔离级别。可选值包括:
available:读取所有可用的数据;
local:读取所有可用且属于当前分片的数据;
majority:读取在大多数节点上提交完成的数据;
linearizable:可线性化读取文档,仅支持从主节点读;
snapshot:读取最近快照中的数据,仅可用于多文档事务
2.writeConcern、readConcern、readPreference 都是 MongoDB startTransaction 的参数,所以是在开启事务的时候配置它。
3.springboot使用@Transactional或@Seata来实现mongodb事务的时候,如何设置writeConcern, readConcern参数?
使用Spring Boot和MongoDB实现事务时,可以通过在方法上使用@Transactional或@Seata注解来设置writeConcern和readConcern参数。
a.使用@Transactional注解:
@Transactional(writeConcern = “majority”, readConcern = “local”)
public void yourMethod() {
// 在此处编写你的业务逻辑
}
b.使用@Seata注解:
@Seata(writeConcern = “majority”, readConcern = “local”)
public void yourMethod() {
// 在此处编写你的业务逻辑
}
在上述代码中,writeConcern参数用于设置写操作的确认级别,可以设置为majority表示大多数节点确认写操作后才返回成功。readConcern参数用于设置读操作的一致性级别,可以设置为local表示读取本地节点的最新数据。
事务超时
在执行事务的过程中,如果操作太多,或者存在一些长时间的等待,则可能会产生异常:
原因在于,默认情况下MongoDB会为每个事务设置1分钟的超时时间,如果在该时间内没有提交,就会强制将其终止。该超时时间可以通过transactionLifetimeLimitSecond变量设定。
事务写机制
MongoDB 的事务错误处理机制不同于关系数据库
1.当一个事务开始后,如果事务要修改的文档在事务外部被修改过,则事务修改这个 文档时会触发 Abort 错误,因为此时的修改冲突了。 这种情况下,只需要简单地重做事务就可以了;
2.如果一个事务已经开始修改一个文档,在事务以外尝试修改同一个文档,则事务以外的修改会等待事务完成才能继续进行。
MongoDB 的事务错误处理机制不同于关系数据库,怎么不同于关系数据库?
MongoDB的事务错误处理机制与传统的关系数据库有几个显著的不同之处:
a.乐观并发控制 vs 悲观并发控制:
MongoDB(乐观并发控制):MongoDB在事务执行期间不会对事务涉及的文档进行显式的锁定。它假设冲突发生的概率较低,因此不进行阻塞式的锁定操作。如果事务提交时检测到其他事务已经修改了事务涉及的文档,则会引发Abort错误,要求应用程序重新执行事务。这种方式可以提高并发性能,但要求应用程序能够处理事务中断和重试。
传统关系数据库(悲观并发控制):关系数据库通常使用悲观并发控制,通过显式的锁定机制(如行级锁、表级锁)来确保事务的隔离性和一致性。当一个事务开始时,会锁定所需的数据,直到事务完成才释放锁。这种方式可以简化应用程序的处理逻辑,但可能会影响并发性能,特别是在高并发环境下。
b.事务隔离级别的实现方式:
MongoDB:MongoDB支持的事务隔离级别较为简单,通常是基于文档级别的隔离,即事务只会在文档层面进行隔离。这意味着如果事务涉及多个文档,某些文档可能会被其他事务同时修改,需要应用程序自行处理冲突。
传统关系数据库:关系数据库支持更为复杂的事务隔离级别,如读未提交、读已提交、可重复读和串行化。这些隔离级别确保了事务在读取和修改数据时的可见性和一致性,通过锁定和多版本并发控制(MVCC)来实现。
c.错误处理机制:
MongoDB:当事务发生冲突时(如外部修改),MongoDB会立即引发Abort错误,要求应用程序重新执行事务。这种实时检测和处理冲突的方式相对简单高效,但也要求应用程序能够处理事务失败后的重试逻辑。
传统关系数据库:在传统关系数据库中,事务发生冲突时(如锁冲突),数据库会根据事务隔离级别和锁策略来处理。通常是阻塞等待或者抛出异常,应用程序需要根据异常类型进行相应的处理,如回滚事务或者重试。
总体而言,MongoDB的事务错误处理机制更加适应大规模分布式系统和高并发环境,通过乐观并发控制和实时的冲突检测来提高性能和扩展性。这种设计不同于传统关系数据库的悲观并发控制和复杂的隔离级别实现,因此在应用程序开发时需要特别注意处理事务中断和冲突的方式。
注意事项:
可以实现和关系型数据库类似的事务场景
必须使用与 MongoDB 4.2 兼容的驱动;
事务默认必须在 60 秒内完成,否则将被取消;我们也可以调整这个默认值,但是建议不要超过60s。
涉及事务的分片不能使用仲裁节点,仲裁节点在分片集群中的作用主要是参与选举和投票,而不存储数据或参与数据的读写操作。
事务会影响 chunk 迁移效率。正在迁移的 chunk 也可能造成事务提交失败(重试即可);
多文档事务中的读操作必须使用主节点读.主要是为了保证事务的数据强一致性。
MongoDB调优
三大导致MongoDB性能不佳的原因:
- 慢查询
- 阻塞等待
- 硬件资源不足
1,2通常是因为模型/索引设计不佳导致的
排查思路:按1-2-3依次排查
Sharding-Sphere
分库分表与多数据源切换区别?
多数据源切换目的是为了动态切换数据源。而分库分表目的不是为了切换动态数据源切换,而是为了解决数据库单一实例的容量限制和性能瓶颈通过分片来提升数据库的性能和存储数据量,它是一种解决方案,它更加强大。
ShardingJDBC主要作用是数据分片和读写分离。
多数据源切换方案(推荐第一种)
1.使用baomidou dynamic-datasource开源组件。
引入dynamic-datasource-spring-boot-starter
通过注解@Ds声明
2.通过mybatis插件切换数据源
3.使用AOP+自定义注解的方式实现多数据源
ElasticSearch
全文检索
通过一个程序扫描文本中的每一个单词,针对单词建立索引,并保存该单词在文本中的位置、以及出现的次数
用户查询时,通过之前建立好的索引来查询,将索引中单词对应的文本位置、出现的次数返回给用户,因为有了具体文本的位置,所以就可以将具体内容读取出来了
倒排索引
当数据写入 ES 时,数据将会通过分词被切分为不同的 term,ES 将 term 与其对应的文档列表建立一种映射关系,这种结构就是倒排索引。
针对单词建立的索引为倒排索引。
平时我们使用的都是索引,都是通过主键定位到某条数据,那么倒排索引呢,刚好相反,数据(单词)对应到主键。然后通过主键获取对应的数据。
搜索的时候先分词,再从倒排索引表中查找。
倒排索引结构:
为了进一步提升索引的效率,ES 在 term 的基础上利用 term 的前缀或者后缀构建了 term index, 用于对 term 本身进行索引,ES 实际的索引结构如下图所示:
这样当我们去搜索某个关键词时(分词后,搜索分词时,通过前/后缀快速找到词项-字典信息),ES 首先根据它的前缀或者后缀迅速缩小关键词的在 term dictionary 中的范围,大大减少了磁盘IO的次数。
PoistionList表示[1,2,3,5,6,9,10],而不是所有的[1,2,3,5,6,9,10]、[12、15]…。其中的1表示的是文档的id, 即[1,2,3,5,6,9,10]为文档id的集合。
实际上,PoistionList不只记录文档id,还记录了其他信息,比如position、offset等,所以Posting的实际结构应该如下:
[docId;tf;position;offset,docId;tf;position;offset,docId;tf;position;offset,docId;tf;position;offset]
1.单词词典(Term Dictionary) :记录所有文档的单词,记录单词到倒排列表的关联关系
常用字典数据结构:
https://www.cnblogs.com/LBSer/p/4119841.html
2.倒排列表(Posting List)-记录了单词对应的文档结合,由倒排索引项组成
3.倒排索引项(Posting):
文档ID
词频TF–该单词在文档中出现的次数,用于相关性评分
位置(Position)-单词在文档中分词的位置。用于短语搜索
(match phrase query)
偏移(Offset)-记录单词的开始结束位置,实现高亮显示
Elasticsearch 的JSON文档中的每个字段,都有自己的倒排索引。
可以指定对某些字段不做索引:
优点︰节省存储空间
缺点: 字段无法被搜索
ElasticSearch简介
ElasticSearch(简称ES)是一个分布式、RESTful 风格的搜索和数据分析引擎,是用Java开发并且是当前最流行的开源的企业级搜索引擎,能够达到近实时搜索,稳定,可靠,快速,安装使用方便。
客户端支持Java、.NET(C#)、PHP、Python、Ruby等多种语言。
Elasticsearch是构建在Apache Lucene之上的开源分布式搜索引擎。
1.Lucene
Lucene具有高性能、易扩展的优点
Lucene的局限性︰
只能基于Java语言开发
类库的接口学习曲线陡峭
原生并不支持水平扩展
2.Elasticsearch 与 Lucene 核心库竞争的优势在于:
完美封装了 Lucene 核心库,设计了友好的 Restful-API,开发者无需过多关注底层机制,直接开箱即用。
分片与副本机制,直接解决了集群下性能与高可用问题。
ElasticSearch简单使用
ElasticSearch文件目录结构

plugins放一些分词插件等

ElasticSearch基本概念
1.索引(Index,相当于database)
一个索引由一个名字来标识(必须全部是小写字母的)
2.Type(类似表,7.0开始被废弃)
3.文档(Document,相当于Row)
Elasticsearch是面向文档的,文档是所有可搜索数据的最小单位。
4.Filed(字段,相当于Column)
ElasticSearch索引操作(即数据库操作)
创建索引
索引命名必须小写,不能以下划线开头
格式: PUT /索引名称
#创建索引
PUT /es_db
#创建索引时可以设置分片数和副本数
PUT /es_db
{
“settings” : {8 “number_of_shards” : 3,
“number_of_replicas” : 2
}
}
#修改索引配置
PUT /es_db/_settings
{
“index” : {
“number_of_replicas” : 1
}
}
查询索引
格式: GET /索引名称
#查询索引
GET /es_db
#es_db是否存在
HEAD /es_db
删除索引
格式: DELETE /索引名称
DELETE /es_db
ElasticSearch文档操作
1.添加(索引)文档
格式: [PUT | POST] /索引名称/[_doc | _create ]/id
注意:POST和PUT都能起到创建/更新的作用,PUT需要对一个具体的资源进行操作也就是
要确定id才能进行更新/创建,而POST是可以针对整个资源集合进行操作的,如果不写id就
由ES生成一个唯一id进行创建新文档,如果填了id那就针对这个id的文档进行创建/更新
2.修改文档
全量更新,整个json都会替换,格式: [PUT | POST] /索引名称/_doc/id
如果文档存在,现有文档会被删除,新的文档会被索引
使用_update部分更新,格式: POST /索引名称/_update/id
update不会删除原来的文档,而是实现真正的数据更新
使用 _update_by_query 更新文档
3.并发场景下修改文档
_seq_no和_primary_term是对_version的优化,7.X版本的ES默认使用这种方式控制版本,所以当在高并发环境下使用乐观锁机制修改文档时,要带上当前文档的_seq_no和_primary_term进行更新。
如果版本号不对,会抛出版本冲突异常。
4.ES Search API提供了两种条件查询搜索方式:
a.REST风格的请求URI,直接将参数带过去
b.封装到request body中,这种方式可以定义更加易读的JSON格式
4.ElasticSearch文档批量操作
a.批量写入
批量对文档进行写操作是通过_bulk的API来实现的
请求方式:POST
请求地址:_bulk
请求参数:通过_bulk操作文档,一般至少有两行参数(或偶数行参数)
第一行参数为指定操作的类型及操作的对象(index,type和id)
第二行参数才是操作的数据
b.批量读取
es的批量查询可以使用mget和msearch两种。其中mget是需要我们知道它的id,可以指定不同的index,也可以指定返回值source。msearch可以通过字段查询来进行一个批量的查找。
ES集群和数据分片
1.ES集群指有多个ES Server(ES节点),集群当中的节点需要是奇数,集群选举采用raft协议。
一个ES Server(ES节点)可以包含多个lucene实例。
数据是存储在分片上的,1个数据分片对应lucene实例,即一个分片是一个lucene实例。
es最大级别的资源是集群,集群是由节点组成的;节点分布在一台一台的机器上;节点里边包含了N个分片;一个分片是一个lucene实例,lucene是真正处理数据的部分,是一个独立的库,不属于es,它可以单独使用;分片中,是有一个一个的段组成的,前边说过es的写入是用到了日志合并树的概念,数据写入先到堆内存的index buffer中,攒一波,然后写到文件存储系统上,然后再落到磁盘上。所以检索的最小单元是段。这里要注意,对es来说,es一次检索,资源分配的最小单元是分片。因为请求会给每个分片分配一个线程。
2.一个es节点可能包含多个主分片(实例),但是最好是一个es节点1个主分片。
3.索引的配置:

主分片数3表示索引会存在3个分片上,P0、P1、P2。
副本分片数1表示每个主分片都有一个副本分片P0-R0、P1-R1、P2-R2
分片越少越好,就不用同步那么多数据
4.索引、分片、节点的关系:
a.节点即es节点,一个节点下包含多个分片。
b.在ElasticSearch中,每创建一个索引(数据库)就会生成多个分片,其实每个分片就是一个Lucene索引(实例),所以一个ElasticSearch索引本质就是用多个Lucene索引构成的。
即索引表示的范围的大于分片的。
c.索引的各个分片是可以分布在不同的节点的。
Elastic Stack介绍
ElasticStack=ELK(Elasticsearch,Logstash,Kibana)+Beats(比如fileBeat,fileBeat相对于Logstash更轻量级)
在Elastic Stack之前我们听说过ELK,ELK分别是Elasticsearch,Logstash,Kibana这三款软件在一起的简称,在发展的过程中又有新的成员Beats(比如fileBeat,fileBeat相对于Logstash更轻量级)的加入,就形成了ElasticStack。

在Elastic Stack生态圈中Elasticsearch作为数据存储和搜索,是生态圈的基石,Kibana在上层提供用户一个可视化及操作的界面,Logstash和Beat可以对数据进行收集。在上图的右侧X-Pack部分则是Elastic公司提供的商业项目。
ElasticSearch 高级查询语法Query DSL
ElasticSearch聚合查询
ElasticSearch搜索原理
ElasticSearch集群架构原理
ElasticSearch建模
ES要掌握什么(关于es下面部分,后面补充):
- 使用:查询语法,聚合操作语法,理解相关性算分(文档匹配度)
- 优化: 文档建模,语法选择,ES集群架构优化,数据处理
二&spm=1001.2101.3001.5002&articleId=138403371&d=1&t=3&u=11240c669697412198204300e6ec2cb5)
258

被折叠的 条评论
为什么被折叠?



