繁体   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