MongoDB08-优化MongoDB

  • 如果使用了错误的数据结构,或者并未在集合中创建正确的索引,MongoDB的速度可能急剧下降。
  • 如果数据库服务器的内存太小或者驱动(CUP或磁盘I/O)速度太低,就可能会对数据库性能产生巨大影响。
  • 对于磁盘,MongoDB公司推荐使用SSD组成的RAID10(即有了性能也有了冗余)。

1、MongoDB的存储引擎

  • 到目前为止,MongoDB近期历史上最大的改变是增加了新存储引擎的API。随着这个API还发布了几个存储引擎,其中,WiredTiger是最重要的。
  • 在MongoDB3.0中,可以通过--storageEngine命令行参数或配置文件的storage.engine指定要使用的存储引擎。
    • MMAPv1存储引擎是MongoDB 3.0之前的存储引擎。
    • WiredTiger存储引擎在MongoDB 3.0引入,并在MongoDB 3.2成为默认的存储引擎
  • 在启动MongoDB时就要选择好要使用的存储引擎,否则后面更换存储引擎会比较麻烦。大多数情况下,最好使用默认的存储引擎
  • 更换存储引擎的步骤:
    • 转储所有的数据,并删除所有数据库。
    • 使用选择好的存储引擎启动MongoDB。
    • 导入转储的所有数据。
  • WiredTiger与MMAPv1存储引擎相比:
    • WiredTiger使用MVCC(多版本并发控制)模型,允许MongoDB支持文档级别的锁定,而MMAPv1只能做到集合级别的锁定。
    • WiredTiger可以管理自己使用的内存,而MMAPvl将内存管理委托给操作系统的内核。
    • WiredTiger可以使用压缩算法对数据进行自动压缩。

1.1、MMAPv1使用内存的方式

  • MMAPv1存储引警使用内存映射文件I/O来访问数据存储,这会同时受操作系统(OS)类型和内存大小的限制。
  • 内存映射文件的需要注意的第一点:在64位操作系统中,Linux中的最大文件可以达到128TB左右(Linux虚拟内存地址限制),Windows对内存映射文件的限制使得最大文件最多可以达到8TB(启用日志之后则只有4TB可用)。在32位操作系统中,最大文件被限制为2GB,所以不推荐使用32位操作系统运行,除非只用于小型开发环境。
  • 内存映射文件的需要注意的第二点:内存映射文件将使用操作系统的虚拟内存,系统将按需把数据文件的某个部分映射到RAM中。
    • 这会产生一种有点令人吃惊的印象:MongoDB将会用尽系统的所有RAM。其实并不是这样,因为MongoDB将与其他应用一起共享虚拟地址空间。并且OS也会按需将内存释放给其他进程。
    • 使用空闲内存的总数作为过度消耗内存的标志并不是个好的实践,因为好的OS只保留很少的(或者压根儿没有)“空闲”内存,所有昂贵的内存都将用于缓存硬盘I/O。空闲内存是对内存的浪费。
  • 通过提供合适大小的内存,MongoDB可以在内存中保持更多它所需的数据,这将减少对磁盘的访问。
  • 通常,为MongoDB分配的内存越多,它的运行速度就越快。不过,如果数据库大小只有2GB,那么添加超过3GB的内存并不会加快它的运行速度,因为整个数据库都已经在内存中了。
  • MMAPv1的工作集大小:MongoDB实例中存储的数据量(“在正常使用过程中”将访问到的数据)。
  • 工作集大小的计算是一种主观方式,并且难以得到准确值。因为对于大多数MongoDB实例,通常只会访问到一部分数据。了解正常工作中使用的是哪部分数据,可以帮助正确地计算出硬件的大小,从而提高MongoDB的性能。

1.2、WiredTiger使用内存的方式

  • WiredTiger使用的内存模型会把尽可能多的“相关”数据有效地保存在内存中。
  • 在MongoDB术语中,内存中存储文档的这个空间称为缓存。默认情况下,MongoDB使用大约一半的物理内存,用于缓存文档。可以通过--wiredTigerCacheSizeGB命令行参数或配置文件的storage.wiredTiger.engineConfig.cacheSizeGB指定缓存的大小。
  • 默认的缓存大小适合大多数系统,修改这个值通常是有害的。但是,如果专用服务器有足够的内存,就可以(彻底)测试缓存更大的运行情况。
    • 注意,缓存仅是用于存储文档的内存量,用于保持连接、运行数据库内部操作、执行用户操作的所有内存在其他地方计算,所以切勿把100%的系统内存都分配给MongoDB,否则操作系统将停止数据库过程!
  • WiredTiger可以压缩磁盘上的数据,压缩算法有:
    • none:禁用压缩
    • snappy:提供了良好的压缩率,CPU使用的开销非常低。
    • zlib:与snappy相比,以更高CPU使用率提供更高的压缩率。
    • zstd(MongoDB 4.2):与zlib相比,以更低CPU使用率提供更高的压缩率。
  • 可以压缩的MongoDB数据:
    • 集合中的数据。
    • 索引数据,即索引中的数据。
    • 日志数据,用于确保数据有冗余,可以恢复,它们会写入长效数据存储器。
  • 压缩算法在启动MongoDB实例时,已经配置好了。修改压缩算法后只影响新创建的数据对象,已经存在的数据对象将继续使用创建它们的压缩算法。例如,如果用默认的snappy压缩算法创建一个集合,然后决定给集合使用zlib压缩算法,现有的集合不会切换到snappy,但任何新的集合都使用zlib选项。
    • 压缩日志(Journal)数据,使用storage.wiredTiger.engineConfig.journalCompressor配置,可用值有:none、snappy(默认)、zlib、zstd。
    • 压缩索引(Index)数据:使用storage.wiredTiger.indexConfig.prefixCompression配置启用或禁用压缩索引数据。若为true(默认),则启用索引前缀压缩。
    • 压缩集合(Collection)数据:使用storage.wiredTiger.collectionConfig.blockCompressor配置,可用值有:none、snappy(默认)、zlib、zstd。

2、评估查询性能

  • MongoDB有两个用于分析查询性能的工具:explain()和MongoDB分析器。
    • MongoDB分析器可以提供慢查询日志,将超过一定执行时间(默认100ms)的查询写到日志文件中。
    • explain()可以判断一个查询的执行性能。

2.1、MongoDB分析器

  • MongoDB分析器将记录统计信息,以及所有符合触发条件的查询。可以通过--profile和--slowms命令行参数。也可以添加到mongodb.conf文件中:
    • operationProfiling.mode:指定哪些操作是profiled,值有:off(默认)、slowOp或all。
    • operationProfiling.slowOpThresholdMs:慢查询的时间阈值,以毫秒为单位,默认值是100。
  • MongoDB启用分析器在一个特殊的固定大小集合system.profile(默认大小是1024KB)中,为每个查询插入一个含有性能和执行细节的文档。
  • 警告:启用MongoDB分析器会影响服务器的性能,所以不应一直在生产环境中运行它,除非正在分析一些已经发现的问题。

1、使用MongoDB分析器

  • db.setProfilingLevel:启用、禁用或配置MongoDB分析器。
    • MongoDB分析器会捕获并记录写操作、游标和数据库命令的性能数据。
    • 如果MongoDB分析器被禁用,该函数会将慢查询记录到诊断日志中。
    • 如果MongoDB分析器级别为1或2(特别是启用了数据库分析程序),则slowms、sampleRate会影响分析程序和诊断日志的行为。
    • 如果MongoDB分析器级别为0(特别是禁用了数据库分析器),则slowms和sampleRate只影响诊断日志。
  • setProfilingLevel函数的基本语法格式如下
db.setProfilingLevel(<level>, <options>)
  • validate函数的参数:
    • <level>:
      • 0:分析器处于关闭状态,不收集任何数据。这是默认的分析器级别。
      • 1:分析器为耗时超过slowms值或匹配筛选器的操作收集数据。
        • 当设置过滤器时:
          • slowms和sampleRate选项不用于分析。
          • 分析器只捕获匹配过滤器的操作。
      • 2:分析器为所有操作收集数据。
    • <options>:
      • slowms
      • sampleRate
      • filter
  • MongoDB分析器使用方法
    • 注意,下面的操作仅对当前数据库有效。
//禁用MongoDB分析器
db.setProfilingLevel(0)

//启用MongoDB分析器,记录超过100毫秒(默认值)的查询
db.setProfilingLevel(1)
//启用MongoDB分析器,记录超过10毫秒的查询
db.setProfilingLevel(1,10)

//启用MongoDB分析器,记录所有操作
db.setProfilingLevel(2)

实例:

//设置分析器记录所有的操作
> db.setProfilingLevel(2)
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }

> db.people.find()
{ "_id" : ObjectId("63280de5b3be3694f6f15380"), "name" : "zhangsan", "age" : 11, "addr" : "shanghai" }
...

//查看分析器记录的内容
> db.system.profile.find()
{
	"op" : "query",                     #显示操作的类型;可以是查询、插入、更新、命令或删除操作
	"ns" : "db1.people",                #查询所在的完整名称空间
	"command" : {
		"find" : "people",
		"filter" : {
			
		},
		"lsid" : {
			"id" : UUID("08345356-256e-4224-92b2-d395aa065e63")
		},
		"$db" : "db1"
	},
	"keysExamined" : 0,
	"docsExamined" : 4,
	"cursorExhausted" : true,
	"numYield" : 0,                     #该查询为其他查询让出锁的次数
	"nreturned" : 4,                    #返回文档的数目
	"locks" : {
		"FeatureCompatibilityVersion" : {
			"acquireCount" : {
				"r" : NumberLong(1)
			}
		},
		"Global" : {
			"acquireCount" : {
				"r" : NumberLong(1)
			}
		},
		"Mutex" : {
			"acquireCount" : {
				"r" : NumberLong(1)
			}
		}
	},
	"flowControl" : {
		
	},
	"responseLength" : 470,                        #响应的字节长度
	"protocol" : "op_msg",
	"millis" : 0,                                  #执行查询所花费的毫秒数
	"planSummary" : "COLLSCAN",
	"execStats" : {
		"stage" : "COLLSCAN",
		"nReturned" : 4,
		"executionTimeMillisEstimate" : 0,
		"works" : 6,
		"advanced" : 4,
		"needTime" : 1,
		"needYield" : 0,
		"saveState" : 0,
		"restoreState" : 0,
		"isEOF" : 1,
		"direction" : "forward",
		"docsExamined" : 4
	},
	"ts" : ISODate("2022-09-19T07:14:33.305Z"),    #以UTC格式显示出查询执行时的时间戮
	"client" : "127.0.0.1",                        #运行该查询的客户端的连接信息
	"appName" : "MongoDB Shell",
	"allUsers" : [ ],
	"user" : ""                                    #运行该操作的用户
}

2、设置分析器集合的大小

//(1)禁用当前数据库上的分析器,确保不会对system.profile有写入操作
> db.setProfilingLevel(0)
{ "was" : 2, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }
//(2)删除system.profile集合
> db.system.profile.drop()
true
//(3)创建一个固定集合,大小为50MB
> db.createCollection("system.profile", {capped:true, size:50*1024*1024})
{ "ok" : 1 }
//(4)启用MongoDB分析器
> db.setProfilingLevel(2)
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }

2.2、使用explain()分析的查询

  • 如果怀疑某个查询的性能不佳,可以使用explain()检查MongoDB是如何执行该查询的。
  • 在查询中添加explain()后,MongoDB将返回一个文档来描述它是如何处理该查询的。
  • explain的基本使用方法如下
//使用explain()函数
db.collection.find().explain()

//使用$explain操作符
db.collection.find()._addSpecial( "$explain", 1 )
db.collection.find( { $query: {}, $explain: 1 } )

示例:

> db.people.find().explain()
{
	"explainVersion" : "1",              #输出格式版本(例如,"1")
	"queryPlanner" : {                   #执行查询的细节,包括计划的细节
		"namespace" : "db1.people",
		"indexFilterSet" : false,    #表明是否使用索引过滤器来实现这个查询
		"parsedQuery" : {            #正在运行的查询。这是查询修改后的形式,显示了如何在内部评估它
			
		},
		"queryHash" : "8B3D4AB8",
		"planCacheKey" : "D542626C",
		"maxIndexedOrSolutionsReached" : false,
		"maxIndexedAndSolutionsReached" : false,
		"maxScansToExplodeReached" : false,
		"winningPlan" : {            #被选中来执行查询的计划
			"stage" : "COLLSCAN",
			"direction" : "forward"
		},
		"rejectedPlans" : [ ]
	},
	"command" : {                        #详细说明了正在执行的命令;
		"find" : "people",
		"filter" : {
			
		},
		"$db" : "db1"
	},
	"serverInfo" : {                     #执行该查询的服务器
		"host" : "11",
		"port" : 27017,
		"version" : "5.0.11",
		"gitVersion" : "d08c3c41c105cde798ca934e3ac3426ac11b57c3"
	},
	"serverParameters" : {               #详细说明了内部参数
		"internalQueryFacetBufferSizeBytes" : 104857600,
		"internalQueryFacetMaxOutputDocSizeBytes" : 104857600,
		"internalLookupStageIntermediateDocumentMaxSizeBytes" : 104857600,
		"internalDocumentSourceGroupMaxMemoryBytes" : 104857600,
		"internalQueryMaxBlockingSortMemoryUsageBytes" : 104857600,
		"internalQueryProhibitBlockingMergeOnMongoS" : 0,
		"internalQueryMaxAddToSetBytes" : 104857600,
		"internalDocumentSourceSetWindowFieldsMaxMemoryBytes" : 104857600
	},
	"ok" : 1
}

1

#                                                                                                                         #
posted @ 2022-09-16 23:45  麦恒  阅读(218)  评论(0编辑  收藏  举报