激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數據庫技術|

服務器之家 - 數據庫 - Mysql - 關于MySQL分區表的一個性能BUG

關于MySQL分區表的一個性能BUG

2021-11-14 18:49老葉茶館 Mysql

這篇文章主要給大家講訴MySQL分區表的一個性能BUG,也就是使用分區表進行數據查詢/加載的時候比普通表的性能下降了約50%,下面就來講將對此的解決辦法,需要的朋友可以參考以下內容

一、問題描述

最近遇到一個問題,也就是使用分區表進行數據查詢/加載的時候比普通表的性能下降了約50%,主要瓶頸出現在CPU,既然是CPU瓶頸理所當然的我們可以采集perf top -a -gpstack來尋找性能瓶頸所在,同時和普通表進行對比,發現CPU主要耗在函數build_template_field上如下圖:

關于MySQL分區表的一個性能BUG

 

二、使用pt-pmap進行棧分析

為了和perf top -g -a進行相互印證,我們同時獲取了當時的pstack,由于線程較多為了方便獲取有用的信息我們通過pt-pmap進行了格式化如下:

關于MySQL分區表的一個性能BUG

格式化后我們提出掉空閑的等待棧,發現大量的如上,這也和perf top -a -g中的表現進行了相互印證。

 

三、關于本列中瓶頸點的分析

我們看到這里大量的cpu耗在

ha_innobase::build_template
 ->build_template_field
   ->dict_col_get_clust_pos

對于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 就出現了上面的邏輯,

大概邏輯如下:

循環表中每個字段(一層循環)ha_innobase::build_template
  是否為需要訪問的字段 build_template_needs_field
  這里包含查詢和寫入的所有字段,需要訪問的字段越多越慢
   如果不是則不作繼續循環
  如果需要訪問
  build_template_field(mysql_row_templ_t結構體填充)
    循環主鍵的每個字段(二層循環)
    包含偽列,主鍵就是表的里面全部字段,表中字段越多越慢)dict_col_get_clust_pos  
      確認本字段在主鍵的位置
      pos0 主鍵 pos1 DB_TRX_ID pos2 DB_ROLL_PTR pos3 開始為用戶其他字段
    循環索引的每個字段(二層循環,但是索引字段一般不會太多,因此這里不會慢)dict_index_t::get_col_pos
      確認本字段在索引的位置,如果沒有則返回NULL
      返回pos 比如 主鍵 id1  二級索引 id2 id3 二級索引為 pos0 id2 pos1 id3 pos2 id1
    繼續完成其他屬性比如mysql null位圖,mysql顯示長度,mysql字符集等等

這里我們看到這里實際上有2層循環,也就是循環套循環(時間復雜度O(M×N)),而循環影響最大的有2個地方:

  • 第一層,表中字段的多少
  • 第二層,需要訪問的字段(讀和寫都算)在主鍵(也就是全部字段)中循環

這里也就是為什么這里會慢的原因。但是template通常不會一個查詢進行多次建立,比如一個普通表的大查詢,只有在語句第一次進行數據定位之前會進行建立,這就不得不說這是分區表和普通表的對比中一個特殊的地方了。下面描述一下。

 

四、分區表中多次建立template的情況

假設我們有如下的分區表:

create table t(
    id1 int,
    id2 int,
    primary key(id1),
    key(id2)
)engine=innodb
partition by range(id1)(
    partition p0 values less than(100),
    partition p1 values less than(200),
    partition p2 values less than(300));    

insert into t values(1,1);
insert into t values(101,1);
insert into t values(201,1);
insert into t values(2,2);
insert into t values(3,2);
insert into t values(4,2);
insert into t values(7,2);
insert into t values(8,2);
insert into t values(9,2);
insert into t values(10,2);

我們使用語句"select * from t where id2=1",顯然id2是二級索引,由于MySQL全部都是local分區的二級索引,因此這里值分別分布在3個分區中,對于這樣一個語句在本該是普通表通過上次定位后的位置繼續訪問(next_same)的時候,通過封裝分區表的方法,將其改為了index read再次定位,而我們可以清楚的看到這里是scan next partition,其part=1這是第二個分區了,也就是我們的p1(第一個為0)

關于MySQL分區表的一個性能BUG

這樣template需要每個分區(scan next partition)都進行重建,這樣就出現了我們上面的問題。這個其實也可以理解,新的分區是新的innodb文件,這樣上次定位的持久化游標實際已經沒有什么用了,就相當于一次新的表訪問。這里在是否進行template建立還有一個判斷如下:

  if (m_prebuilt->sql_stat_start) {
    build_template(false);
  }

而m_prebuilt->sql_stat_start除了在語句開始的時候設置為true,每次更換分區依舊會設置為true如下:

ha_innopart::set_partition:
m_prebuilt->sql_stat_start = m_sql_stat_start_parts.test(part_id);

 

五、關于一個特殊的流程

在我們的故障pstack中還有一個棧如下:

關于MySQL分區表的一個性能BUG

這個棧實際并不完整,但是其中出現了Partition_helper::handle_ordered_index_scan,這個函數實際上和分區表的排序有關,如果我們考慮這樣一種情況,對于二級索引select max(id2) from t,那么需要首先訪問每個分區獲取其中的最大值然后對比每個分區的最大值,得到最終的結果,而MySQL則采用優先隊列進行處理,這應該是就是本函數完成的部分功能(沒仔細去看)。其次我們先出現了QUICK_RANGE_SELECT這是范圍查詢會用到的,那么我們構造如下:

select * from t where id2<2 order by id2;


棧:

關于MySQL分區表的一個性能BUG

這里就是因為id2這個字段只保證在分區內部是按照大小排列的但是在整個表來講,它是無序的,需要額外的處理。

 

六、問題模擬

有了這些準備,我們可以構造一個300個字段和25個分區的分區表。測試版本最新8.0.26

create table tpar300col(
    id1 int,
    id2 int,
    id3 int,
    id4 int,
...
    id299 varchar(20),
    id300 varchar(20),
    primary key(id1),
    key(id2)
)engine=innodb
partition by range(id1)(
    partition p0 values less than(100),
    partition p1 values less than(200),
    partition p3 values less than(300),
 ...
    partition p25 values less than(2500));  

insert into tpar300col values(1  ,1,1,
....每個分區一條數據
insert into tpar300col values(2401,1,1

然后構造一些其他數據id2不要為1,建立存儲過程:

delimiter //

CREATE PROCEDURE test300col()
begin 
  declare num int;
  set num = 1; 
while num <= 1000000 do
  select * from tpar300col where id2=1;
  set num = num+1;
end while;
end //
執行:
 /opt/mysql/mysql3340/install/mysql8/bin/mysql -S--socket=/opt/mysql/mgr3315/data/mgr3315.sock -e"use test;call test300col();" > log.log

然后perf top 觀察如下:

關于MySQL分區表的一個性能BUG

這樣問題就得到了確認。

 

七、總結

這個問題實際上和二級索引相對于分區鍵的數據離散度有關,但是我們無法控制二級索引的數據,并且索引也是必須使用的。只能通過一些方面盡量避免,當然我也提交了一個BUG,如下:

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

不知道是否有辦法修復這個問題,比如對于分區表來講實際上每個分區的字段都是一樣的,是否需要每次都重建mysql_row_templ_t.clust_rec_field_no?如果不需要那么問題自解,官方目前已經驗證了這個問題確實存在。如下是一些避免的方式,

  • 分區表字段不宜過多
  • 訪問的字段不應該一味的使用select *
  • 避免使用hash分區,hash分區會增加這種問題

到此這篇關于講訴MySQL分區表的一個性能BUG的文章就介紹到這了,更多相關MySQL分區表的一個性能BUG內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.tuicool.com/articles/EZFjAfa

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产999精品久久久久 | 看a级毛片| 亚洲欧美国产精品va在线观看 | 一级片久久免费 | 中文字幕在线观看视频www | 99re66热这里只有精品8 | 午夜视频免费在线观看 | 双性精h调教灌尿打屁股的文案 | 人人看人人舔 | 亚洲精品com | 精品久久久久久久久久久久久久 | 国产九九九九 | 久久久一区二区三区精品 | 黄色影院在线观看视频 | 久久久久亚洲视频 | 91av网址| 爽爽淫人网 | 国产精品久久久久久久久久久久久久久久 | 嗯哈~不行好大h双性 | 日韩毛片免费观看 | 国产精品视频yy9299一区 | 手机国产乱子伦精品视频 | 性视频久久 | 欧美成人精品一区二区三区 | va免费视频 | av在线收看 | 亚州综合| 欧美四级在线观看 | av在线一区二区三区四区 | 亚洲免费毛片基地 | 羞羞视频入口 | 久久成人免费网站 | 国产一国产精品一级毛片 | 毛片在线免费 | 久久久无码精品亚洲日韩按摩 | 美女擦逼| 欧美一级做性受免费大片免费 | 在线看免费观看av | 黄色影院在线 | 999精品国产 | 精品小视频 |