我的MySQL/MariaDB InnoDB/XtraDB出现了奇怪的行为。最近切换到MariaDB 5.5。交换机使服务器整体性能更好,但我仍然有这个问题。
一个特定的表索引似乎不时会中断。过了一段时间,它自己修复了它。
SHOW CREATE TABLE article_inventory;
给出
CREATE TABLE `article_inventory` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`article_variant_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`season_id` BIGINT(20) UNSIGNED NOT NULL,
`warehouse_id` BIGINT(20) UNSIGNED NOT NULL,
`quantity` BIGINT(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE INDEX `unique_inventory_idx` (`article_variant_id`, `season_id`, `warehouse_id`),
INDEX `article_variant_id_idx` (`article_variant_id`),
INDEX `article_inventory_season_id_idx` (`season_id`),
INDEX `article_inventory_warehouse_id_idx` (`warehouse_id`),
CONSTRAINT `article_inventory_article_variant_id_article_variant_id` FOREIGN KEY (`article_variant_id`) REFERENCES `article_variant` (`id`),
CONSTRAINT `article_inventory_season_id_season_id` FOREIGN KEY (`season_id`) REFERENCES `season` (`id`),
CONSTRAINT `article_inventory_warehouse_id_warehouse_id` FOREIGN KEY (`warehouse_id`) REFERENCES `warehouse` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=3827622858;
EDIT:大多数SELECT查询都是针对此表进行的。每小时都会有重大更新。有时会有非常大的更新。
运行此查询:
SELECT a.id
FROM article a
INNER JOIN article_variant a2
ON a.style_id = a2.style_id
INNER JOIN article_block a3
ON a2.po = a3.po
INNER JOIN color c
ON a2.color_id = c.id
INNER JOIN size s
ON a2.size_id = s.id
INNER JOIN article_group a4
ON a2.id = a4.article_variant_id AND (a4.season_id = 6)
INNER JOIN article_inventory a5
ON a2.id = a5.article_variant_id AND (((a5.warehouse_id = 5 OR a5.warehouse_id = 1) AND a5.season_id = 6))
INNER JOIN article_date a6
ON a.style_id = a6.style_id AND ((a6.pricelist_id = 5 AND a6.season_id = 6))
INNER JOIN article_price a7
ON a.style_id = a7.style_id AND ((a7.pricelist_id = 5 AND a7.season_id = 6))
INNER JOIN pricelist p
ON a7.pricelist_id = p.id
INNER JOIN concept c2
ON a4.concept_id = c2.id
INNER JOIN category c3
ON a4.category_id = c3.id
LEFT JOIN order_cart_row o
ON a2.id = o.article_variant_id AND (o.order_id = 17035)
LEFT JOIN shortlist s2
ON a.id = s2.article_id AND (s2.order_id = 17035)
WHERE ((a2.is_canceled <> 1 AND a4.is_canceled <> 1) OR o.quantity IS NOT NULL) AND c2.id = 2
GROUP BY a.id
应该在大约0.5-1.0秒内执行,并给我一个类似的解释:
id select_type table type possible_keys key key_len ref rowsExtra
1 SIMPLE p const PRIMARY PRIMARY 8 const 1 Using index; Using temporary; Using filesort
1 SIMPLE c2 const PRIMARY PRIMARY 8 const 1 Using index
1 SIMPLE a3 index PRIMARY PRIMARY 98 NULL 1031Using where
1 SIMPLE a2 ref PRIMARY,unique_variant_idx,color_id_idx,style_id_idx,size_id_idx,article_variant_po_idx article_variant_po_idx 98 wsp_stage.a3.po 14 Using where
1 SIMPLE s eq_ref PRIMARY PRIMARY 11 wsp_stage.a2.size_id 1 Using index
1 SIMPLE c eq_ref PRIMARY PRIMARY 11 wsp_stage.a2.color_id 1
1 SIMPLE o eq_ref unique_rows_idx,article_variant_id_idx,order_id_idx unique_rows_idx 16 const,wsp_stage.a2.id 1 Using index
1 SIMPLE a eq_ref unique_style_idx unique_style_idx 767 wsp_stage.a2.style_id 1 Using index
1 SIMPLE a6 ref article_season_pricelist_unique_idx,season_id_idx,pricelist_id_idx,style_id_idx article_season_pricelist_unique_idx 784 wsp_stage.a2.style_id,const,const 1 Using index
1 SIMPLE a7 ref article_season_pricelist_unique_idx,season_id_idx,pricelist_id_idx,style_id_idx article_season_pricelist_unique_idx 784 wsp_stage.a2.style_id,const,const 1 Using index
1 SIMPLE a4 eq_ref unique_group_idx,one_per_season_idx,category_id_idx,concept_id_idx,season_id_idx,article_variant_id_idx one_per_season_idx 16 wsp_stage.a2.id,const 1 Using index
1 SIMPLE c3 eq_ref PRIMARY PRIMARY 8 wsp_stage.a4.category_id 1 Using index
1 SIMPLE s2 ref shortlist_article_id_idx shortlist_article_id_idx 8 wsp_stage.a.id 10 Using where
1 SIMPLE a5 ref unique_inventory_idx,article_variant_id_idx,article_inventory_season_id_idx,article_inventory_warehouse_id_iunique_inventory_idx 17 wsp_stage.a2.id,const 8 Using where
当一切正常工作时,article_inventory(别名a5)使用unique_inventory_idx
或article_variant_id_idx
。两者都应该给我大约5-100个检查行。
但偶尔会发生一些事情,同样的查询需要大约30秒,这给了我这样的解释:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE p const PRIMARY PRIMARY 8 const 1 Using index; Using temporary; Using filesort
1 SIMPLE c2 const PRIMARY PRIMARY 8 const 1 Using index
1 SIMPLE a5 ref unique_inventory_idx,article_variant_id_idx,article_inventory_season_id_idx,article_inventory_warehouse_id_iarticle_inventory_season_id_idx 8 const 6718732 Using where
1 SIMPLE a4 eq_ref unique_group_idx,one_per_season_idx,category_id_idx,concept_id_idx,season_id_idx,article_variant_id_idx one_per_season_idx 16 wsp_stage.a5.article_variant_id,const1 Using where
1 SIMPLE c3 eq_ref PRIMARY PRIMARY 8 wsp_stage.a4.category_id 1 Using index
1 SIMPLE a2 eq_ref PRIMARY,unique_variant_idx,color_id_idx,style_id_idx,size_id_idx,article_variant_po_idx PRIMARY 8 wsp_stage.a5.article_variant_id 1
1 SIMPLE c eq_ref PRIMARY PRIMARY 11 wsp_stage.a2.color_id 1 Using index
1 SIMPLE a eq_ref unique_style_idx unique_style_idx 767 wsp_stage.a2.style_id 1 Using index
1 SIMPLE a6 ref article_season_pricelist_unique_idx,season_id_idx,pricelist_id_idx,style_id_idx article_season_pricelist_unique_idx 784 wsp_stage.a2.style_id,const,const 1 Using index
1 SIMPLE a7 ref article_season_pricelist_unique_idx,season_id_idx,pricelist_id_idx,style_id_idx article_season_pricelist_unique_idx 784 wsp_stage.a2.style_id,const,const 1 Using index
1 SIMPLE s eq_ref PRIMARY PRIMARY 11 wsp_stage.a2.size_id 1 Using index
1 SIMPLE a3 eq_ref PRIMARY PRIMARY 98 wsp_stage.a2.po 1 Using index
1 SIMPLE o eq_ref unique_rows_idx,article_variant_id_idx,order_id_idx unique_rows_idx 16 const,wsp_stage.a5.article_variant_id1 Using where
1 SIMPLE s2 ref shortlist_article_id_idx shortlist_article_id_idx 8 wsp_stage.a.id 7 Using where
article_inventory
(a5)现在使用article_inventory_season_id_idx
。这是一个非常非常糟糕的指数,因为它是所有指数中第二不具体的。给我6718732行检查。
my.ini:
[mysqld]
datadir="W:/mariadb/data/"
port=3306
sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION"
default_storage_engine=innodb
innodb_buffer_pool_size=5000M
innodb_log_file_size=52428800
innodb_file_per_table
innodb_file_format=Barracuda
[client]
port=3307
首先,索引没有被破坏。这可能与表上的MySQL统计信息建议查询优化器使用错误的索引有关。现在,在我们讨论可能的解决方案之前,让我们先了解它的原因
当MySQL运行查询时,它会查看该表的统计信息,以确定哪些索引适合查询,然后根据建议选择正确的索引。表统计信息包含诸如索引基数和与使用索引相关联的成本等信息。MySQL每次运行查询时都会查看这些统计信息,以确定最佳执行路径。
现在,由于索引是存储在磁盘上的实际数据结构,当您更新、插入和删除这些索引时,它们的统计信息会发生变化。这可能是问题的根本原因。InnoDB通过对索引结构进行8次随机深度挖掘来动态更新统计数据。MyISAM的做法不同。有关此方面的更多信息,请参阅此链接:https://dba.stackexchange.com/questions/3398/from-where-does-the-mysql-query-optimizer-read-index-statistics
你提到过你每隔一段时间就会进行一次重复更新来进行大型插入。我怀疑,无论是在插入期间还是插入之后,都会有一小段时间,表的innodb统计信息过期或正在编译中。这可能就是为什么你会看到从一个指数到感染指数的零星变化。此时您的统计信息不正确,查询优化器做出了错误的选择。
去谷歌以下内容:
mysql statistics update
这里有一大堆链接,里面有更多关于这方面的细节,这是一本很好的读物。
我以前在数据库中看到过这种情况,它不是BUG,只是需要注意的事情。
可能的解决方案:
- 在使用on duplicate update语句进行大容量插入之后,对有问题的表显式调用ANALYSE TABLE。在更新后直接运行此命令可能会将统计信息转换为正确的形式,从而建议使用正确的索引。不利的一面是,您的系统实际上可能会重新编译统计信息两次,这会浪费资源。请记住,我不确定这个问题是发生在插入语句之后还是期间
- 强制在select语句中使用正确的索引。您可以强制MySQL始终使用正确的索引。然而,这是个坏主意。在某个时刻,另一个索引可能会对您的查询进行更优化,并且由于您现在正在有效地硬编码索引以用于查询,因此稍后会成为一个问题
- 保持原样,这听起来可能很奇怪,但运行30秒的查询是灾难吗?这可能取决于您的要求,但如果查询运行30秒是可以的,为什么要尝试修复它?记住,如果它没有坏,就不要修理它的哲学
如果你需要更多的澄清,我希望这是有意义的。。。。。