mongodb聚合查询优化_【MongoDB】MongoDB 性能优化 - BI查询聚合
最全的Java后端知識(shí)體系每天更新中...。
在BI服務(wù)中通過查詢聚合語句分析定位慢查詢/聚合分析,小結(jié)如下:
慢查詢定位:
通過Profile分析慢查詢
對(duì)于查詢優(yōu)化:
通過添加相應(yīng)索引提升查詢速度;
對(duì)于聚合大數(shù)據(jù)方案:
首先要說明的一個(gè)問題是,對(duì)于OLAP型的操作,期望不應(yīng)該太高。畢竟是對(duì)于大量數(shù)據(jù)的操作,光從IO就已經(jīng)遠(yuǎn)超通常的OLTP操作,所以要求達(dá)到OLTP操作的速度和并發(fā)是不現(xiàn)實(shí)的,也是沒有意義的。但并不是說一點(diǎn)優(yōu)化空間也沒有。
這樣優(yōu)化之后預(yù)計(jì)在可以提升一部分查詢性能,但是并不能解決。原因開頭說了,對(duì)OLAP就不能期望這么高,應(yīng)該從源頭入手,考慮:
1) 每次eventType字段和insertTime有更新或插入時(shí)就做好計(jì)數(shù)
2) 每隔一段時(shí)間做一次完整的統(tǒng)計(jì),緩存統(tǒng)計(jì)結(jié)果,查詢的時(shí)候直接展現(xiàn)給用戶
問題描述
執(zhí)行BI服務(wù)的接口, 發(fā)現(xiàn)返回一天的記錄需要10s左右,這明顯是有問題:
問題定位
定位慢查詢
為了定位查詢,需要查看當(dāng)前mongo profile的級(jí)別, profile的級(jí)別有0|1|2,分別代表意思:0代表關(guān)閉,1代表記錄慢命令,2代表全部
db.getProfilingLevel()
顯示為0, 表示默認(rèn)下是沒有記錄的。
設(shè)置profile級(jí)別,設(shè)置為記錄慢查詢模式, 所有超過1000ms的查詢語句都會(huì)被記錄下來
db.setProfilingLevel(1, 1000)
再次執(zhí)行BI一天的查詢接口,查看Profile, 發(fā)現(xiàn)確實(shí)記錄了這條慢查詢:
分析慢查詢語句
通過view document查看慢查詢的profile記錄
{
"op" : "command",
"ns" : "standalone.application_alert",
"command" : {
"aggregate" : "application_alert",
"pipeline" : [
{
"$match" : {
"factoryId" : "10001",
"$and" : [
{
"insertTime" : {
"$gte" : ISODate("2018-03-25T16:00:00.000Z"),
"$lte" : ISODate("2018-03-26T09:04:20.288Z")
}
}
]
}
},
{
"$project" : {
"eventType" : 1,
"date" : {
"$concat" : [
{
"$substr" : [
{
"$year" : [
"$insertTime"
]
},
0,
4
]
},
"-",
{
"$substr" : [
{
"$month" : [
"$insertTime"
]
},
0,
2
]
},
"-",
{
"$substr" : [
{
"$dayOfMonth" : [
"$insertTime"
]
},
0,
2
]
}
]
}
}
},
{
"$group" : {
"_id" : {
"date" : "$date",
"eventType" : "$eventType"
},
"count" : {
"$sum" : 1
}
}
}
]
},
"keysExamined" : 0,
"docsExamined" : 2636052,
"numYield" : 20651,
"locks" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(41310)
}
},
"Database" : {
"acquireCount" : {
"r" : NumberLong(20655)
}
},
"Collection" : {
"acquireCount" : {
"r" : NumberLong(20654)
}
}
},
"nreturned" : 0,
"responseLength" : 196,
"protocol" : "op_query",
"millis" : 9484,
"planSummary" : "COLLSCAN",
"ts" : ISODate("2018-03-26T08:44:51.322Z"),
"client" : "10.11.0.118",
"allUsers" : [
{
"user" : "standalone",
"db" : "standalone"
}
],
"user" : "standalone@standalone"
}
從上面profile中可以看到我們執(zhí)行的BI 查詢接口對(duì)應(yīng)到Mongo執(zhí)行了一個(gè)pipleline:
第一步: match 工廠ID是10001的記錄,時(shí)間段是當(dāng)前一天
{
"$match" : {
"factoryId" : "10001",
"$and" : [
{
"insertTime" : {
"$gte" : ISODate("2018-03-25T16:00:00.000Z"),
"$lte" : ISODate("2018-03-26T09:04:20.288Z")
}
}
]
}
},
第二步: 字段映射,project:
{
"$project" : {
"eventType" : 1,
"date" : {
"$concat" : [
{
"$substr" : [
{
"$year" : [
"$insertTime"
]
},
0,
4
]
},
"-",
{
"$substr" : [
{
"$month" : [
"$insertTime"
]
},
0,
2
]
},
"-",
{
"$substr" : [
{
"$dayOfMonth" : [
"$insertTime"
]
},
0,
2
]
}
]
}
}
},
可以看到除了對(duì)event_type做了簡(jiǎn)單的project外,還對(duì)insertTime字段做了拼接,拼接為yyyy-MM-dd格式,并且project為date字段。
第三步: group操作
{
"$group" : {
"_id" : {
"date" : "$date",
"eventType" : "$eventType"
},
"count" : {
"$sum" : 1
}
}
對(duì)#2中的date和event_type進(jìn)行g(shù)roup,統(tǒng)計(jì)不同日期和事件類型所對(duì)應(yīng)的事件數(shù)量(count).
對(duì)應(yīng)的其它字段:
Mills: 花了9484毫秒返回查詢結(jié)果
ts:命令執(zhí)行時(shí)間
info:命令的內(nèi)容
query:代表查詢
ns: standalone.application_alert 代表查詢的庫與集合
nreturned:返回記錄數(shù)及用時(shí)
reslen:返回的結(jié)果集大小,byte數(shù)
nscanned:掃描記錄數(shù)量
如果發(fā)現(xiàn)9484毫秒時(shí)間比較長(zhǎng),那么就需要作優(yōu)化。
通常來說,經(jīng)驗(yàn)上可以對(duì)這些指標(biāo)做參考:
比如nscanned數(shù)很大,或者接近記錄總數(shù),那么可能沒有用到索引查詢。
reslen很大,有可能返回沒必要的字段。
nreturned很大,那么有可能查詢的時(shí)候沒有加限制。
查看DB/Server/Collection的狀態(tài)
DB status
查看Server狀態(tài)
由于server 狀態(tài)指標(biāo)眾多,我這邊只列出來一部分。
{
"host" : "OPASTORMON", #主機(jī)名
"version" : "3.4.1", #版本號(hào)
"process" : "mongod", #進(jìn)程名
"pid" : NumberLong(1462), #進(jìn)程ID
"uptime" : 10111875.0, #運(yùn)行時(shí)間
"uptimeMillis" : NumberLong(10111875602), #運(yùn)行時(shí)間
"uptimeEstimate" : NumberLong(10111875), #運(yùn)行時(shí)間
"localTime" : ISODate("2018-03-26T09:14:13.679Z"), #當(dāng)前時(shí)間
"asserts" : {
"regular" : 0,
"warning" : 0,
"msg" : 0,
"user" : 26549,
"rollovers" : 0
},
"connections" : {
"current" : 104, #當(dāng)前鏈接數(shù)
"available" : 715, #可用鏈接數(shù)
"totalCreated" : 11275
},
"extra_info" : {
"note" : "fields vary by platform",
"page_faults" : 49
},
"globalLock" : {
"totalTime" : NumberLong(10111875549000), #總運(yùn)行時(shí)間(ns)
"currentQueue" : {
"total" : 0, #當(dāng)前需要執(zhí)行的隊(duì)列
"readers" : 0, #讀隊(duì)列
"writers" : 0 #寫隊(duì)列
},
"activeClients" : {
"total" : 110, #當(dāng)前客戶端執(zhí)行的鏈接數(shù)
"readers" : 0, #讀鏈接數(shù)
"writers" : 0 #寫鏈接數(shù)
}
},
"locks" : {
"Global" : {
"acquireCount" : {
"r" : NumberLong(8457368136),
"w" : NumberLong(1025512487),
"W" : NumberLong(7)
},
"acquireWaitCount" : {
"r" : NumberLong(2)
},
"timeAcquiringMicros" : {
"r" : NumberLong(94731)
}
},
"Database" : {
"acquireCount" : {
"r" : NumberLong(3715927334),
"w" : NumberLong(1025512452),
"R" : NumberLong(194),
"W" : NumberLong(69)
},
"acquireWaitCount" : {
"r" : NumberLong(13),
"w" : NumberLong(5),
"R" : NumberLong(6),
"W" : NumberLong(3)
},
"timeAcquiringMicros" : {
"r" : NumberLong(530972),
"w" : NumberLong(426173),
"R" : NumberLong(3207),
"W" : NumberLong(1321)
}
},
"Collection" : {
"acquireCount" : {
"r" : NumberLong(3715046899),
"w" : NumberLong(1025512453)
}
},
"Metadata" : {
"acquireCount" : {
"w" : NumberLong(1),
"W" : NumberLong(3)
}
}
},
"network" : {
"bytesIn" : NumberLong(373939915493), #輸入數(shù)據(jù)(byte)
"bytesOut" : NumberLong(961227224728), #輸出數(shù)據(jù)(byte)
"physicalBytesIn" : NumberLong(373939915493),#物理輸入數(shù)據(jù)(byte)
"physicalBytesOut" : NumberLong(961054421482),#物理輸入數(shù)據(jù)(byte)
"numRequests" : NumberLong(3142377739) #請(qǐng)求數(shù)
},
"opLatencies" : {
"reads" : {
"latency" : NumberLong(3270742192035),
"ops" : NumberLong(540111914)
},
"writes" : {
"latency" : NumberLong(261946981235),
"ops" : NumberLong(1024301418)
},
"commands" : {
"latency" : NumberLong(458086641),
"ops" : NumberLong(6776702)
}
},
"opcounters" : {
"insert" : 6846448, #插入操作數(shù)
"query" : 248443106, #查詢操作數(shù)
"update" : 1018594976, #更新操作數(shù)
"delete" : 1830, #刪除操作數(shù)
"getmore" : 162213, #獲取更多的操作數(shù)
"command" : 298306448 #其他命令操作數(shù)
},
"opcountersRepl" : {
"insert" : 0,
"query" : 0,
"update" : 0,
"delete" : 0,
"getmore" : 0,
"command" : 0
},
"storageEngine" : {
"name" : "wiredTiger",
"supportsCommittedReads" : true,
"readOnly" : false,
"persistent" : true
},
"tcmalloc" : {
"generic" : {
"current_allocated_bytes" : NumberLong(3819325752),
"heap_size" : NumberLong(6959509504)
},
"tcmalloc" : {
"pageheap_free_bytes" : 199692288,
"pageheap_unmapped_bytes" : NumberLong(2738442240),
"max_total_thread_cache_bytes" : NumberLong(1073741824),
"current_total_thread_cache_bytes" : 35895120,
"total_free_bytes" : 202049224,
"central_cache_free_bytes" : 165650360,
"transfer_cache_free_bytes" : 503744,
"thread_cache_free_bytes" : 35895120,
"aggressive_memory_decommit" : 0,
"formattedString" : "------------------------------------------------\nMALLOC: 3819325752 ( 3642.4 MiB) Bytes in use by application\nMALLOC: + 199692288 ( 190.4 MiB) Bytes in page heap freelist\nMALLOC: + 165650360 ( 158.0 MiB) Bytes in central cache freelist\nMALLOC: + 503744 ( 0.5 MiB) Bytes in transfer cache freelist\nMALLOC: + 35895120 ( 34.2 MiB) Bytes in thread cache freelists\nMALLOC: + 40001728 ( 38.1 MiB) Bytes in malloc metadata\nMALLOC: ------------\nMALLOC: = 4261068992 ( 4063.7 MiB) Actual memory used (physical + swap)\nMALLOC: + 2738442240 ( 2611.6 MiB) Bytes released to OS (aka unmapped)\nMALLOC: ------------\nMALLOC: = 6999511232 ( 6675.3 MiB) Virtual address space used\nMALLOC:\nMALLOC: 521339 Spans in use\nMALLOC: 115 Thread heaps in use\nMALLOC: 4096 Tcmalloc page size\n------------------------------------------------\nCall ReleaseFreeMemory() to release freelist memory to the OS (via madvise()).\nBytes released to the OS take up virtual address space but no physical memory.\n"
}
},
"mem" : {
"bits" : 64, #64位系統(tǒng)
"resident" : 4103, #占有物理內(nèi)存數(shù)
"virtual" : 7045, #占有虛擬內(nèi)存
"supported" : true, #是否支持?jǐn)U展內(nèi)存
"mapped" : 0,
"mappedWithJournal" : 0
},
"ok" : 1.0
}
查看application_alert這個(gè)collection的狀態(tài)
{
"ns" : "standalone.application_alert",
"size" : 783852548,
"count" : 2638262,
"avgObjSize" : 297,
"storageSize" : 189296640,
"capped" : false,
"wiredTiger" : {
"metadata" : {
"formatVersion" : 1
},
"creationString" : "allocation_size=4KB,app_metadata=(formatVersion=1),block_allocation=best,block_compressor=snappy,cache_resident=false,checksum=on,colgroups=,collator=,columns=,dictionary=0,encryption=(keyid=,name=),exclusive=false,extractor=,format=btree,huffman_key=,huffman_value=,ignore_in_memory_cache_size=false,immutable=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=q,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=64MB,log=(enabled=true),lsm=(auto_throttle=true,bloom=true,bloom_bit_count=16,bloom_config=,bloom_hash_count=8,bloom_oldest=false,chunk_count_limit=0,chunk_max=5GB,chunk_size=10MB,merge_max=15,merge_min=0),memory_page_max=10m,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,source=,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,type=file,value_format=u",
"type" : "file",
"uri" : "statistics:table:collection-4-6040851502998278747",
"LSM" : {
"bloom filter false positives" : 0,
"bloom filter hits" : 0,
"bloom filter misses" : 0,
"bloom filter pages evicted from cache" : 0,
"bloom filter pages read into cache" : 0,
"bloom filters in the LSM tree" : 0,
"chunks in the LSM tree" : 0,
"highest merge generation in the LSM tree" : 0,
"queries that could have benefited from a Bloom filter that did not exist" : 0,
"sleep for LSM checkpoint throttle" : 0,
"sleep for LSM merge throttle" : 0,
"total size of bloom filters" : 0
},
"block-manager" : {
"allocations requiring file extension" : 31543,
"blocks allocated" : 346110,
"blocks freed" : 124238,
"checkpoint size" : 189259776,
"file allocation unit size" : 4096,
"file bytes available for reuse" : 20480,
"file magic number" : 120897,
"file major version number" : 1,
"file size in bytes" : 189296640,
"minor version number" : 0
},
"btree" : {
"btree checkpoint generation" : 165242,
"column-store fixed-size leaf pages" : 0,
"column-store internal pages" : 0,
"column-store variable-size RLE encoded values" : 0,
"column-store variable-size deleted values" : 0,
"column-store variable-size leaf pages" : 0,
"fixed-record size" : 0,
"maximum internal page key size" : 368,
"maximum internal page size" : 4096,
"maximum leaf page key size" : 2867,
"maximum leaf page size" : 32768,
"maximum leaf page value size" : 67108864,
"maximum tree depth" : 3,
"number of key/value pairs" : 0,
"overflow pages" : 0,
"pages rewritten by compaction" : 0,
"row-store internal pages" : 0,
"row-store leaf pages" : 0
},
"cache" : {
"bytes currently in the cache" : 1014702364,
"bytes read into cache" : 0,
"bytes written from cache" : 1888143292.0,
"checkpoint blocked page eviction" : 0,
"data source pages selected for eviction unable to be evicted" : 0,
"hazard pointer blocked page eviction" : 0,
"in-memory page passed criteria to be split" : 224,
"in-memory page splits" : 112,
"internal pages evicted" : 0,
"internal pages split during eviction" : 0,
"leaf pages split during eviction" : 0,
"modified pages evicted" : 2,
"overflow pages read into cache" : 0,
"overflow values cached in memory" : 0,
"page split during eviction deepened the tree" : 0,
"page written requiring lookaside records" : 0,
"pages read into cache" : 0,
"pages read into cache requiring lookaside entries" : 0,
"pages requested from the cache" : 49191856,
"pages written from cache" : 217176,
"pages written requiring in-memory restoration" : 0,
"unmodified pages evicted" : 0
},
"cache_walk" : {
"Average difference between current eviction generation when the page was last considered" : 0,
"Average on-disk page image size seen" : 0,
"Clean pages currently in cache" : 0,
"Current eviction generation" : 0,
"Dirty pages currently in cache" : 0,
"Entries in the root page" : 0,
"Internal pages currently in cache" : 0,
"Leaf pages currently in cache" : 0,
"Maximum difference between current eviction generation when the page was last considered" : 0,
"Maximum page size seen" : 0,
"Minimum on-disk page image size seen" : 0,
"On-disk page image sizes smaller than a single allocation unit" : 0,
"Pages created in memory and never written" : 0,
"Pages currently queued for eviction" : 0,
"Pages that could not be queued for eviction" : 0,
"Refs skipped during cache traversal" : 0,
"Size of the root page" : 0,
"Total number of pages currently in cache" : 0
},
"compression" : {
"compressed pages read" : 0,
"compressed pages written" : 83604,
"page written failed to compress" : 0,
"page written was too small to compress" : 133572,
"raw compression call failed, additional data available" : 0,
"raw compression call failed, no additional data available" : 0,
"raw compression call succeeded" : 0
},
"cursor" : {
"bulk-loaded cursor-insert calls" : 0,
"create calls" : 78758,
"cursor-insert key and value bytes inserted" : 795578636,
"cursor-remove key bytes removed" : 8857,
"cursor-update value bytes updated" : 0,
"insert calls" : 2642785,
"next calls" : 5850718215.0,
"prev calls" : 3,
"remove calls" : 4460,
"reset calls" : 48942545,
"restarted searches" : 0,
"search calls" : 10229,
"search near calls" : 46285468,
"truncate calls" : 0,
"update calls" : 0
},
"reconciliation" : {
"dictionary matches" : 0,
"fast-path pages deleted" : 0,
"internal page key bytes discarded using suffix compression" : 7946666,
"internal page multi-block writes" : 60010,
"internal-page overflow keys" : 0,
"leaf page key bytes discarded using prefix compression" : 0,
"leaf page multi-block writes" : 64250,
"leaf-page overflow keys" : 0,
"maximum blocks required for a page" : 253,
"overflow values written" : 0,
"page checksum matches" : 10496129,
"page reconciliation calls" : 189077,
"page reconciliation calls for eviction" : 1,
"pages deleted" : 7
},
"session" : {
"object compaction" : 0,
"open cursor count" : 35
},
"transaction" : {
"update conflicts" : 0
}
},
"nindexes" : 1,
"totalIndexSize" : 24420352,
"indexSizes" : {
"_id_" : 24420352
},
"ok" : 1.0
}
性能優(yōu)化
性能優(yōu)化 - 索引
通過上述的指標(biāo),需要優(yōu)化的話,第一考慮的是查看是否對(duì)該collection創(chuàng)建了索引:
查看是否有相關(guān)索引
增加相關(guān)字段的搜索索引
發(fā)現(xiàn)只有對(duì)id的索引,所以接下來對(duì)application_alert創(chuàng)建event_type和factory_id,timeStamp字段的索引
db.application_alert.ensureIndex({"insertTime": 1, "eventType": 1});
db.application_alert.ensureIndex({"insertTime": 1});
db.application_alert.ensureIndex({"eventType": 1});
db.application_alert.ensureIndex({"factoryId": 1});
查看增加index后查詢一天的數(shù)據(jù)聚合需要424ms, 基本可以接受。
查詢20天,看時(shí)間仍然需要20s
通過增加索引小結(jié)
到這里我們基本可以看到添加查詢index對(duì)BI接口的影響,索引的添加只是解決了針對(duì)索引字段查詢的效率,但是并不能解決查詢之后數(shù)據(jù)的聚合問題。對(duì)一天而言由于數(shù)據(jù)量的少,查詢速度提升顯著,但是對(duì)大量數(shù)據(jù)做聚合仍然不合適。
我們通過增加索引解決了什么問題?
在沒有索引的前提下,找出100萬條{eventType: "abnormal"}需要多少時(shí)間?全表掃描COLLSCAN從700w條數(shù)據(jù)中找出600w條,跟從1億條數(shù)據(jù)中找出600w條顯然是兩個(gè)概念。命中索引IXSCAN,這個(gè)差異就會(huì)小很多,幾乎可以忽略。索引的添加只是解決了針對(duì)索引字段查詢的效率,但是并不能解決查詢之后數(shù)據(jù)的聚合問題。順便應(yīng)該提一下看效率是否有差異應(yīng)該看執(zhí)行計(jì)劃,不要看執(zhí)行時(shí)間,時(shí)間是不準(zhǔn)確的。
性能優(yōu)化 - 聚合大量數(shù)據(jù)
那問題是,如何解決這種查詢聚合大量數(shù)據(jù)的問題呢?
首先要說明的一個(gè)問題是,對(duì)于OLAP型的操作,期望不應(yīng)該太高。畢竟是對(duì)于大量數(shù)據(jù)的操作,光從IO就已經(jīng)遠(yuǎn)超通常的OLTP操作,所以要求達(dá)到OLTP操作的速度和并發(fā)是不現(xiàn)實(shí)的,也是沒有意義的。但并不是說一點(diǎn)優(yōu)化空間也沒有。
這樣優(yōu)化之后預(yù)計(jì)在可以提升一部分查詢性能,但是并不能解決。原因開頭說了,對(duì)OLAP就不能期望這么高。如果你真有這方面的需求,就應(yīng)該從源頭入手,考慮:
每次info字段有更新或插入時(shí)就做好計(jì)數(shù)
每隔一段時(shí)間做一次完整的統(tǒng)計(jì),緩存統(tǒng)計(jì)結(jié)果,查詢的時(shí)候直接展現(xiàn)給用戶
總結(jié)
以上是生活随笔為你收集整理的mongodb聚合查询优化_【MongoDB】MongoDB 性能优化 - BI查询聚合的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win11程序兼容性助手如何关闭
- 下一篇: nodejs 获取文件路径_Nodejs