关于mysql分区与未分区速度和带有主键表分区的问题

关于mysql分区与未分区速度和带有主键表分区的问题,第1张

分区的一些优点包括:

1)、与单个磁盘或文件系统分区相比,可以存储更多的数据

2)、对于那些已经失去保存意义的数据,通常可以通过删除与那些数据有关的分区,很容易地删除那些数据。

3)、一些查询可以得到极大的优化,这主要是借助于满足一个给定WHERE语句的数据可以只保存在一个或多个分区内,这样在查找时就不用查找其他剩余的分区。因为分区可以在创建了分区表后进行修改,所以在第一次配置分区方案时还不曾这么做时,可以重新组织数据,来提高那些常用查询的效率。

4)、涉及到例如SUM()和COUNT()这样聚合函数的查询,可以很容易地进行并行处理。这种查询的一个简单例子如 “SELECT salesperson_id, COUNT (orders) as order_total FROM sales GROUP BY salesperson_id;”。通过“并行”,这意味着该查询可以在每个分区上同时进行,最终结果只需通过总计所有分区得到的结果。

5)、通过跨多个磁盘来分散数据查询,来获得更大的查询吞吐量。

这里有一篇文章分析的非常好,请参考:

http://www.cnblogs.com/acpp/archive/2010/08/09/1795464.html

零,什么是数据库分区

来说一下什么是数据库分区,以mysql为例。mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面(可以通过my.cnf中的datadir来查看),一张表主要对应着三个文件,一个是frm存放表结构的,一个是myd存放表数据的,一个是myi存表索引的。如果一张表的数据量太大的话,那么myd,myi就会变的很大,查找数据就会变的很慢,这个时候我们可以利用mysql的分区功能,在物理上将这一张表对应的三个文件,分割成许多个小块,这样呢,我们查找一条数据时,就不用全部查找了,只要知道这条数据在哪一块,然后在那一块找就行了。如果表的数据太大,可能一个磁盘放不下,这个时候,我们可以把数据分配到不同的磁盘里面去。

水平有限,如有误导请谅解。

最近遇到一个问题,也就是使用分区表进行数据查询/加载的时候比普通表的性能下降了约50%,主要瓶颈出现在CPU,既然是CPU瓶颈理所当然的我们可以采集perf top -a -g和pstack来寻找性能瓶颈所在,同时和普通表进行对比,发现CPU主要耗在函数build_template_field上如下图:

为了和perf top -g -a进行相互印证,我们同时获取了当时的pstack,由于线程较多为了方便获取有用的信息我们通过pt-pmap进行了格式化如下:

格式化后我们提出掉空闲的等待栈,发现大量的如上,这也和perf top -a -g中的表现进行了相互印证。

我们看到这里大量的cpu耗在

对于template来讲,其几乎是和特定的一次的查询进行绑定的,也就是普通的语句至少需要一个template。其结构为row_prebuilt_t,包含查询元组,查询的表,查询用到的索引,事务相关信息,持久化游标,MySQL层查询行的长度,自增信息,ICP相关信息,mysql_row_templ_t结构等信息。

其中mysql_row_templ_t 这个信息就是每个字段一个,主要作用记录的是MySQL层feild信息和Innodb层columns信息的相关属性,用于快速转换一行记录在MySQL层和Innodb层之间转换。为了初始化mysql_row_templ_t 就出现了上面的逻辑,大概逻辑如下:

这里我们看到这里实际上有2层循环,也就是循环套循环(时间复杂度O(M×N)),而循环影响最大的有2个地方:

这里也就是为什么这里会慢的原因。但是template通常不会一个查询进行多次建立,比如一个普通表的大查询,只有在语句第一次进行数据定位之前会进行建立,这就不得不说这是分区表和普通表的对比中一个特殊的地方了。下面描述一下。

假设我们有如下的分区表:

我们使用语句"select * from t where id2=1",显然id2是二级索引,由于MySQL全部都是local分区的二级索引,因此这里值分别分布在3个分区中,对于这样一个语句在本该是普通表通过上次定位后的位置继续访问(next_same)的时候,通过封装分区表的方法,将其改为了index read再次定位,而我们可以清楚的看到这里是scan next partition,其part=1这是第二个分区了,也就是我们的p1(第一个为0)

这样template需要每个分区(scan next partition)都进行重建,这样就出现了我们上面的问题。这个其实也可以理解,新的分区是新的innodb文件,这样上次定位的持久化游标实际已经没有什么用了,就相当于一次新的表访问。

这里在是否进行template建立还有一个判断如下:

而m_prebuilt->sql_stat_start除了在语句开始的时候设置为true,每次更换分区依旧会设置为true如下:

在我们的故障pstack中还有一个栈如下:

这个栈实际并不完整,但是其中出现了Partition_helper::handle_ordered_index_scan,这个函数实际上和分区表的排序有关,如果我们考虑这样一种情况,对于二级索引select max(id2) from t,那么需要首先访问每个分区获取其中的最大值然后对比每个分区的最大值,得到最终的结果,而MySQL则采用优先队列进行处理,这应该是就是本函数完成的部分功能(没仔细去看)。

其次我们先出现了QUICK_RANGE_SELECT这是范围查询会用到的,那么我们构造如下:

栈:

这里就是因为id2这个字段只保证在分区内部是按照大小排列的但是在整个表来讲,它是无序的,需要额外的处理。

有了这些准备,我们可以构造一个300个字段和25个分区的分区表。测试版本最新8.0.26

然后perf top 观察如下:

这样问题就得到了确认。

这个问题实际上和二级索引相对于分区键的数据离散度有关,但是我们无法控制二级索引的数据,并且索引也是必须使用的。只能通过一些方面尽量避免,当然我也提交了一个BUG,如下:

https://bugs.mysql.com/bug.php?id=104576

不知道是否有办法修复这个问题,比如对于分区表来讲实际上每个分区的字段都是一样的,是否需要每次都重建mysql_row_templ_t.clust_rec_field_no?如果不需要那么问题自解,但是还需要官方确认和验证,毕竟我对分区表了解有限。如下是一些避免的方式,


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/7289505.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-03
下一篇 2023-04-03

发表评论

登录后才能评论

评论列表(0条)

保存