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函数的基本语法格式如下
1
db.setProfilingLevel(<level>, <options>)
  • validate函数的参数:
    • <level>:
      • 0:分析器处于关闭状态,不收集任何数据。这是默认的分析器级别。
      • 1:分析器为耗时超过slowms值或匹配筛选器的操作收集数据。
        • 当设置过滤器时:
          • slowms和sampleRate选项不用于分析。
          • 分析器只捕获匹配过滤器的操作。
      • 2:分析器为所有操作收集数据。
    • <options>:
      • slowms
      • sampleRate
      • filter
  • MongoDB分析器使用方法
    • 注意,下面的操作仅对当前数据库有效。
1
2
3
4
5
6
7
8
9
10
//禁用MongoDB分析器
db.setProfilingLevel(0)
 
//启用MongoDB分析器,记录超过100毫秒(默认值)的查询
db.setProfilingLevel(1)
//启用MongoDB分析器,记录超过10毫秒的查询
db.setProfilingLevel(1,10)
 
//启用MongoDB分析器,记录所有操作
db.setProfilingLevel(2)

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//设置分析器记录所有的操作
> 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
2
3
4
5
6
7
8
9
10
11
12
//(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的基本使用方法如下
1
2
3
4
5
6
//使用explain()函数
db.collection.find().explain()
 
//使用$explain操作符
db.collection.find()._addSpecial( "$explain", 1 )
db.collection.find( { $query: {}, $explain: 1 } )

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
> 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

1
#                                                                                                                         #
posted @   麦恒  阅读(266)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
点击右上角即可分享
微信分享提示

目录导航