簡體   English   中英

奇怪的行為優化查詢索引(MariaDB + InnoDB)

[英]Weird behavior optimizing query indices (MariaDB + InnoDB)

我目前正在嘗試為一個很大的項目表優化索引,並在解釋結果和實際查詢運行時之間遇到非常相反的直觀行為。

服務器正在運行具有以下配置選項的MariaDB版本10.1.26-MariaDB-0 + deb9u1:

key_buffer_size         = 5G
max_allowed_packet      = 16M
thread_stack            = 192K
thread_cache_size       = 8

myisam_sort_buffer_size = 512M
read_buffer_size        = 2M
read_rnd_buffer_size    = 1M

query_cache_type = 0
query_cache_limit = 256K
query_cache_min_res_unit = 2k
query_cache_size = 0M

join_buffer_size = 8M
sort_buffer_size = 8M
tmp_table_size = 64M
max_heap_table_size = 64M
table_open_cache = 4K
performance_schema = ON
innodb_buffer_pool_size = 30G
innodb_log_buffer_size = 4MB
innodb_log_file_size = 1G
innodb_buffer_pool_instances = 10

該表看起來包含大約680萬行總計12.1GB ,看起來像這樣:

CREATE TABLE `ad_master_test` (
    `ID_AD_MASTER` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    /* Some more attribute fields (mainly integers) ... */
    `FK_KAT` BIGINT(20) UNSIGNED NOT NULL,
    /* Some more content fields (mainly varchars/integers) ... */
    `STAMP_START` DATETIME NULL DEFAULT NULL,
    `STAMP_END` DATETIME NULL DEFAULT NULL,
    PRIMARY KEY (`ID_AD_MASTER`),
    INDEX `TEST1` (`STAMP_START`, `FK_KAT`),
    INDEX `TEST2` (`FK_KAT`, `STAMP_START`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
ROW_FORMAT=DYNAMIC
AUTO_INCREMENT=14149037;

我已經盡可能簡化了查詢,以更好地說明問題。 我正在使用FORCE INDEX在這里說明我的問題。

第一個索引使用了explain語句進行了優化,並且看起來很有希望(關於explain輸出):

SELECT * 
FROM `ad_master_test`
FORCE INDEX (TEST1)
WHERE FK_KAT IN
    (94169,94163,94164,94165,94166,94167,94168,94170,94171,94172,
     94173,94174,94175,94176,94177,94162,99606,94179,94180,94181,
     94182,94183,94184,94185,94186,94187,94188,94189,94190,94191,
     94192,94193,94194,94195,94196,94197,94198,94199,94200,94201,
     94202,94203,94204,94205,94206,94207,94208,94209,94210,94211,
     94212,94213,94214,94215,94216,94217,94218,94219,94220,94221,
     94222,94223,94224,94225,94226,94227,94228,94229,94230,94231,
     94232,94233,94234,94235,94236,94237,94238,94239,94240,94241,
     94178,94161)

ORDER BY STAMP_START DESC
LIMIT 24

結果說明:

id     select_type   table            type     possible_keys   key     key_len    ref        rows      Extra
1      SIMPLE        ad_master_test   index    (NULL)          TEST1   14         (NULL)     24        Using where

和這個配置文件:

Status                  Duration
starting                0.000180
checking permissions    0.000015
Opening tables          0.000041
After opening tables    0.000013
System lock             0.000011
Table lock              0.000013
init                    0.000115
optimizing              0.000044
statistics              0.000050
preparing               0.000039
executing               0.000009
Sorting result          0.000016
Sending data            4.827512
end                     0.000023
query end               0.000008
closing tables          0.000004
Unlocking tables        0.000014
freeing items           0.000011
updating status         0.000132
cleaning up             0.000021

第二個索引只是反轉的字段(我在這里了解的方式: https : //dev.mysql.com/doc/refman/8.0/en/order-by-optimization.html ),這看起來非常可怕(關於解釋輸出):

SELECT * 
FROM `ad_master_test`
FORCE INDEX (TEST2)
WHERE FK_KAT IN (94169,94163,94164,94165,94166,94167,94168,94170,94171,94172,94173,94174,94175,94176,94177,94162,99606,94179,94180,94181,94182,94183,94184,94185,94186,94187,94188,94189,94190,94191,94192,94193,94194,94195,94196,94197,94198,94199,94200,94201,94202,94203,94204,94205,94206,94207,94208,94209,94210,94211,94212,94213,94214,94215,94216,94217,94218,94219,94220,94221,94222,94223,94224,94225,94226,94227,94228,94229,94230,94231,94232,94233,94234,94235,94236,94237,94238,94239,94240,94241,94178,94161)
ORDER BY STAMP_START DESC
LIMIT 24

結果說明:

id     select_type   table            type     possible_keys   key     key_len    ref        rows      Extra
1      SIMPLE        ad_master_test   range    TEST2           TEST2   8          (NULL)     497.766   Using index condition; Using filesort

和這個配置文件:

Status                 Duration
starting               0.000087
checking permissions   0.000007
Opening tables         0.000021
After opening tables   0.000007
System lock            0.000006
Table lock             0.000005
init                   0.000058
optimizing             0.000023
statistics             0.000654
preparing              0.000480
executing              0.000008
Sorting result         0.433607
Sending data           0.001681
end                    0.000010
query end              0.000007
closing tables         0.000003
Unlocking tables       0.000011
freeing items          0.000010
updating status        0.000158
cleaning up            0.000021

編輯:當不使用強制索引時 ,說明如下更改:

id     select_type   table            type     possible_keys   key     key_len    ref        rows      Extra
1      SIMPLE        ad_master_test   index    TEST2           TEST1   14         (NULL)     345       Using where

概要文件和運行時保持(按預期)與在TEST1索引上使用FORCE INDEX時相同。

/編輯

老實說,我無法解決這個問題。 為什么解釋和實際查詢性能差異如此之大。 在5秒鍾的“發送數據”期間,服務器會做什么?

似乎有一些TEXTBLOB甚至是較大的VARCHAR列? 12.1GB / 6.8M = 1.8KB。 如果不需要它們,請不要獲取它們。 這樣可以加快任何此類查詢的速度。 你有多少RAM?

這兩個指數似乎花費了不同的時間(4.8s vs 0.4s)。

STAMP_STARTFK_KAT

這樣可以通過按所需順序掃描索引BTree來避免“文件排序”。 它必須檢查每個條目是否有匹配的fk_kat。 我認為它將在24個匹配行(請參閱LIMIT )之后停止,但是可能是前24個(快速),后24個(非常慢)或介於兩者之間。

FK_KATSTAMP_START

該節目直接進入所有82個ID,掃描每個ID(假定不是唯一的),收集了數百行。 然后執行“文件排序”。 (注意:如果正在提取任何TEXT列,這將是一種磁盤排序。)然后提供前24個。(糟糕;我不認為MariaDB 10.1具有該功能。)

即使這樣做需要更多步驟,但通過避免全索引掃描,事實證明速度更快。

其他注意事項

key_buffer_size = 20G不要使用MyISAM。 但是,如果這樣做,請將其更改為RAM的10%。 如果不這樣做,請將其更改為30M ,並將70%的RAM分配給innodb_buffer_pool_size

如果您想進一步討論,請為每個查詢提供EXPLAIN FORMAT=JSON SELECT ... 這將進行“成本”分析,這應解釋為什么它選擇了較差的指數。

另一個實驗

取而代之的SELECT * ,運行時序和EXPLAINs只用SELECT ID_AD_MASTER 如果證明是“快速”,則重新編寫查詢:

SELECT b.*   -- (or selected columns from `b`)
    FROM ( SELECT ID_AD_MASTER FROM ... ) AS a
    JOIN ad_master_test AS b  USING(ad_master_test)
    ORDER BY STAMP_START DESC ;   -- (yes, repeat the ORDER BY)

您的my.cnf [mysqld]部分要考慮的建議(RPS為RatePerSecond)

thread_handling=pool-of-threads  # from one-thread-per-connection see refman
max_connections=100  # from 151 because max_used_connections < 60
read_rnd_buffer_size=256K  # from 1M to reduce RAM used, < handler_read_rnd_next RPS
aria_pagecache_division_limit=50  # from 100 for WARM cache for < aria_pagecache_reads RPS
key_cache_division_limit=50  # from 100 for WARM cache for < key_reads
key_buffer_size=2G  # from 5G  Mysqltuner reports 1G used (this could be WRONG-test it)
innodb_io_capacity=30000  # from 200 since you have SSD
innodb_buffer_pool_instances=8  # from 16 for your volume of data
innodb_lru_scan_depth=128  # from 1024 to conserve CPU every SECOND see refman
innodb_buffer_pool_size=36G  # from 30G for effective size of 32G when
innodb_change_buffer_pool_size=10  # from 25% set aside for Del,Ins,Upd activities

有關其他建議,請查看配置文件,網絡配置文件,聯系信息(包括我的Skype ID)。 還有其他機會可以改善您的配置。

請記住,每天僅一次更改建議,請監控,如果陽性結果繼續進行下一個建議。 否則,請讓我知道任何嚴重的不利結果,以及哪些更改似乎導致了問題。

VARIABLESGLOBAL STATUS

觀察結果:

  • 版本:10.1.26-MariaDB-0 + deb9u1
  • 64 GB RAM
  • 正常運行時間= 7天22:50:19
  • 您不在Windows上運行。
  • 運行64位版本
  • 您似乎正在完全(或主要)在運行InnoDB。

更重要的問題:

1(或更大)的“平均負載”通常表示查詢效率低。 Created_tmp_disk_tablesHandler_read_rnd_next的較大值進一步證實了這一點,即每秒僅“ 91”次查詢。 讓我們看看最慢的查詢。 請參閱建議以進行進一步調查。

thread_cache_size = 20

擺脫了MyISAM之后,就不需要這么大的key_buffer_size 從5G減少到50M。

我不是ROW_FORMAT=COMPRESSED的粉絲; 這對您的問題有兩個相關影響:增加用於壓縮/解壓縮的CPU,並且需要額外的buffer_pool空間。 另一方面, GLOBAL STATUS並不表示30GB太“小”。 是否需要減少磁盤空間使用量?

您已關閉一些優化? 這是對其他問題的回應嗎?

詳細信息和其他觀察結果:

( (key_buffer_size - 1.2 * Key_blocks_used * 1024) / _ram ) = (5120M - 1.2 * 25 * 1024) / 65536M = 7.8%中浪費了RAM的百分比。 -減少key_buffer_size。

( Key_blocks_used * 1024 / key_buffer_size ) = 25 * 1024 / 5120M = 0.00% -使用的( Key_blocks_used * 1024 / key_buffer_size ) = 25 * 1024 / 5120M = 0.00%百分比。 高水位標記。 -降低key_buffer_size以避免不必要的內存使用。

( innodb_buffer_pool_size / _ram ) = 30720M / 65536M = 46.9% -用於InnoDB buffer_pool的RAM的百分比

( table_open_cache ) = 4,096要緩存的表描述符的數量-通常好幾百個。

( Innodb_os_log_written / (Uptime / 3600) / innodb_log_files_in_group / innodb_log_file_size ) = 6,714,002,432 / (687019 / 3600) / 2 / 1024M = 0.0164比率-(請參閱分鍾數)

( Uptime / 60 * innodb_log_file_size / Innodb_os_log_written ) = 687,019 / 60 * 1024M / 6714002432 = 1,831 -InnoDB日志輪換之間的分鍾數從5.6.8開始,可以動態更改; 一定還要更改my.cnf。 -(建議每兩次輪換60分鍾有點隨意。)調整innodb_log_file_size。 (無法在AWS中更改。)

( default_tmp_storage_engine ) = default_tmp_storage_engine =

( Innodb_rows_deleted / Innodb_rows_inserted ) = 1,319,619 / 2015717 = 0.655流失-“不要排隊,就去做。” (如果將MySQL用作隊列。)

( innodb_thread_concurrency ) = 0 =讓InnoDB為concurrency_tickets確定最佳選擇。 -設置為0或64。這可能會減少CPU使用率。

( innodb_print_all_deadlocks ) = innodb_print_all_deadlocks = OFF是否記錄所有死鎖。 -如果您遇到死鎖困擾,請將其打開。 警告:如果您有很多死鎖,則可能會向磁盤寫入很多死鎖。

( innodb_buffer_pool_populate ) = OFF = 0 -NUMA控制

( query_prealloc_size / _ram ) = 24,576 / 65536M = 0.00% -用於解析。 內存的Pct

( query_alloc_block_size / _ram ) = 16,384 / 65536M = 0.00% -用於解析。 內存的Pct

( net_buffer_length / max_allowed_packet ) = 16,384 / 16M = 0.10%

( bulk_insert_buffer_size / _ram ) = 8M / 65536M = 0.01% -多行INSERT和LOAD DATA的緩沖區-太大可能會威脅RAM大小。 太小會阻礙這種操作。

( Created_tmp_tables ) = 19,436,364 / 687019 = 28 /sec創建“ temp”表作為復雜SELECT的一部分的頻率。

( Created_tmp_disk_tables ) = 17,887,832 / 687019 = 26 /sec創建磁盤 “ temp”表作為復雜SELECT的一部分的頻率-增加tmp_table_size和max_heap_table_size。 在使用MEMORY代替MyISAM時檢查臨時表的規則。 較小的架構或查詢更改也許可以避免使用MyISAM。 更好的索引和重新編制查詢更有可能會有所幫助。

( Created_tmp_disk_tables / Questions ) = 17,887,832 / 62591791 = 28.6% -需要磁盤tmp表的查詢的百分比。 -更好的索引/無斑點/等

( Created_tmp_disk_tables / Created_tmp_tables ) = 17,887,832 / 19436364 = 92.0% -溢出到磁盤的臨時表的百分比-也許增加tmp_table_size和max_heap_table_size; 改善指標; 避免斑點等

( tmp_table_size ) = 64M用於支持SELECT的MEMORY臨時表的大小限制-減小tmp_table_size以避免RAM耗盡。 也許不超過64M。

( Handler_read_rnd_next ) = 703,386,895,308 / 687019 = 1023824 /sec -如果大量表掃描,則為高-可能是鍵不足

( Handler_read_rnd_next / Com_select ) = 703,386,895,308 / 58493862 = 12,024每個SELECT掃描的平均行數。 (大約)-考慮提高read_buffer_size

( Select_full_join ) = 15,981,913 / 687019 = 23 /sec不帶索引的連接-向JOIN中使用的表中添加合適的索引。

( Select_full_join / Com_select ) = 15,981,913 / 58493862 = 27.3% -無索引聯接選擇的百分比-在JOIN中使用的表中添加適當的索引。

( Select_scan ) = 1,510,902 / 687019 = 2.2 /sec全表掃描-添加索引/優化查詢(除非它們是小表)

( sort_buffer_size ) = 8M每個線程分配一個,按最大大小分配給內存,直到5.6.4,因此保持較低; 之后,更大就可以了。 -這可能正在占用可用的RAM; 建議不要超過2M。

( binlog_format ) = binlog_format = STATEMENT -STATEMENT / ROW / MIXED。 ROW是首選; 它可能成為默認值。

( slow_query_log ) = slow_query_log = OFF是否記錄慢速查詢。 (5.1.12)

( long_query_time ) = 10定義“慢速”查詢的截止時間(秒)。 -建議2

( Threads_created / Connections ) = 3,081 / 303642 = 1.0% -進程創建的速度-增加thread_cache_size(非Windows)

異常大:

Connection_errors_peer_address = 2
Handler_icp_attempts = 71206 /sec
Handler_icp_match = 71206 /sec
Handler_read_next / Handler_read_key = 283
Handler_read_prev = 12522 /sec
Handler_read_rnd_deleted = 16 /sec
Innodb_rows_read = 1255832 /sec
Key_blocks_unused = 4.24e+6
Performance_schema_table_instances_lost = 32
Select_range / Com_select = 33.1%
Sort_scan = 27 /sec
Tc_log_page_size = 4,096
innodb_lru_scan_depth / innodb_io_capacity = 5.12
innodb_max_dirty_pages_pct_lwm = 0.10%
max_relay_log_size = 100MB
myisam_sort_buffer_size = 512MB

字符串異常:

Compression = ON
innodb_cleaner_lsn_age_factor = HIGH_CHECKPOINT
innodb_empty_free_list_algorithm = BACKOFF
innodb_fast_shutdown = 1
innodb_foreground_preflush = EXPONENTIAL_BACKOFF
innodb_log_checksum_algorithm = INNODB
myisam_stats_method = NULLS_UNEQUAL
opt_s__engine_condition_pushdown = off
opt_s__mrr = off
opt_s__mrr_cost_based = off

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM