æ¬æå享èªå为äºç¤¾åºããå为äºMySQLææ¯ä¸æ ãGaussDB(for MySQL)ä¸MySQLçCOUNTæ¥è¯¢å¹¶è¡ä¼åçç¥-äºç¤¾åº-å为äºãï¼ä½è ï¼GaussDB æ°æ®åºã
1.èæ¯ä»ç»
ç»è®¡è¡¨çè¡æ°(COUNT)æ¯å®¢æ·åºç¨åDBAè¿ç»´å¸¸ç¨çæä½ãMySQLè½æ¯ä¸ç广æ³ä½¿ç¨çOLTPæ°æ®åºï¼ä½å¤§è¡¨æ§è¡COUNTæä½éå¸¸èæ¶ï¼åå å¨äºï¼
(1) COUNTæä½éè¦éå表çå ¨éæ°æ®æ¥è·å精确çè¡æ°ï¼å½è¡¨æ°æ®éè¾å¤§æé¨åæ°æ®ä¸å¨Buffer Poolæ¶ï¼æ¥è¯¢æä½å¾èæ¶ã
(2) MySQL 8.0.14ä¹åççæ¬æ å¹¶è¡æ¥è¯¢ææ¯ï¼åªè½ä¸²è¡æ§è¡SQLè¯å¥ï¼æ æ³å©ç¨å¤æ ¸ææ¯è¿è¡å éã
(3) MySQL 8.0.14ååç»çæ¬InnoDBåå¨å¼ææ¯æå¹¶è¡æ«æä¸»é®ï¼ä½ä¸æ¯æå¹¶è¡æ«æäºçº§ç´¢å¼ï¼å¨ä¸»é®å¾å¤§ãäºçº§ç´¢å¼è¾å°çåºæ¯ä¸ï¼ç¸æ¯èçæ¬(MySQL 5.7)ä¸²è¡æ«æäºçº§ç´¢å¼ï¼ç¤¾åºçæ¬å¹¶è¡æ«æå¯è½åºç°æ§è½å£åï¼å¹¶ä¸ä¸æ¯æå ³éå¹¶è¡æ«æä¸»é®ç¹æ§ã
GaussDB(for MySQL)éè¿èªç å¹¶è¡æ¥è¯¢(PQ)å计ç®ä¸æ¨(NDP)ç¹æ§ï¼è§£å³äºå¤§è¡¨COUNTæ ¢çé®é¢ï¼å ¸ååºæ¯ä¸ï¼ç¸æ¯MySQLå¹¶è¡æ«æä¸»é®æ§è½å¯æåè¶ è¿80åã
2. MySQL COUNTå¹¶è¡ä»ç»
MySQL8.0.14çæ¬InnoDBåå¨å¼ææ¯æå¹¶è¡æ«æä¸»é®ï¼è¿æ ·å¯ä»¥å©ç¨å¹¶è¡çè½å对COUNTæä½è¿è¡å éï¼ç¹æ§è¯´æåè§å¾1ã
å¾1 MySQL 8.0 InnoDBåå¨å¼æå¹¶è¡æ«æä¸»é®ç¹æ§
2.1åçä»ç»
MySQL COUNTå¹¶è¡å¨InnoDBåå¨å¼æå±å®ç°çæ¡æ¶å¾åè§å¾2ãä¼åå¨å³çèµ°COUNTå¹¶è¡åï¼çæCOUNTå¹¶è¡ç®åâUnqualifiedCountIteratorâï¼ è°ç¨handler APIæ¥å£âhandler::ha_recordsâï¼InnoDBå±å¨å½æ°âParallel_reader::parallel_readâä¸è°åº¦worker线ç¨è¿è¡æåãæ«æãè®¡æ°æ±æ»ã
å¾2 InnoDB å¹¶è¡æ«æè°åº¦é»è¾
ä¸é¢åºäºMySQL 8.0.14æºç ï¼ä»ç»COUNTå¹¶è¡å¨SQL弿åInnoDBåå¨å¼æä¸çå®ç°ã
2.1.1 COUNTå¹¶è¡å¨SQL弿ä¸çå®ç°
ï¼1ï¼SQL弿å±å¨ä¼åé¶æ®µå¤æSQLæ¯å¦ä¸ºç®åçCOUNTï¼è®°å½å¨åéâJOIN:: select_countâä¸ï¼åéçå®ä¹åè§ä¸æ¹ä»£ç ã
/*
When join->select_count is set, tables will not be optimized away.
The call to records() will be delayed until the execution phase and the counting will be done on an index of Optimizer's choice.
The index will be decided in find_shortest_key(), called from
optimize_aggregated_query().
*/
bool JOIN::select_count{false};
ï¼2ï¼SQL弿å±å¨çææ§è¡è®¡åé¶æ®µï¼å¤æåéâJOIN::select_countâçå¼ï¼å¦æåéå¼ä¸ºTRUEï¼åçæå¹¶è¡COUNTç®åâUnqualifiedCountIteratorâï¼ç¨æ·å¯ä»¥éè¿âEXPLAIN FORMAT=TREEâæâEXPLAIN ANALYZEâå½ä»¤æ¥çæ§è¡è®¡åï¼å¦æå å«âCount rowsâå ³é®å说æ COUNT å¹¶è¡çæï¼åè§ä¸é¢çæ§è¡è®¡åã
mysql> explain format=tree select count(*) from lineitem\G
*************************** 1. row ***************************
EXPLAIN: -> Count rows in lineitem
2.1.2 COUNTå¹¶è¡å¨InnoDB åå¨å¼æä¸çå®ç°
(1) SQL弿è°ç¨handler API æ¥å£âhandler::ha_recordsâï¼ä¼ éä¼åå¨éæ©çç´¢å¼ç»InnoDBåå¨å¼æï¼è·åCOUNTç»æã
(2) InnoDBåå¨å¼æåªæ¯æä¸»é®çå¹¶è¡æ«æï¼å½æ°âha_innobase::records_from_indexâ忽ç¥ç´¢å¼ä¿¡æ¯ï¼å¼ºå¶éæ©ä¸»é®è¿è¡å¹¶è¡æ«æã
(3) InnoDBåå¨å¼æå¨å½æ°âParallel_reader::parallel_readâä¸å¯¹ä¸»é®ç´¢å¼è¿è¡åæ¥åçï¼å¹¶è°åº¦ worker 线ç¨å¯¹åçè¿ä¸æ¥æåãæ«æã计æ°ã
(4) æä»¬æInnoDBä¸ååºâhandler::ha_recordsâæ¥å£å¹¶è°åº¦workerè¿è¡å·¥ä½çç线ç¨ç§°ä¸ºleader线ç¨ï¼leader线ç¨è°ç¨å æ ä¿¡æ¯å¦ä¸ï¼
UnqualifiedCountIterator::Read
get_exact_record_count
handler::ha_records
ha_innobase::records_from_index
ha_innobase::records
row_scan_index_for_mysql
row_mysql_parallel_select_count_star
Parallel_reader::run
Parallel_reader::parallel_read
(5) æä»¬æInnoDBä¸ååºâParallel_reader::workerâæ¥å£å¹¶è¿è¡æ«æã计æ°å·¥ä½ç线ç¨ç§°ä¸ºworker线ç¨ï¼worker线ç¨çå¹¶å度å¯ä»¥éè¿åæ°â innodb_parallel_read_threadsâæ§å¶ï¼worker线ç¨è°ç¨å æ ä¿¡æ¯å¦ä¸ï¼
Parallel_reader::worker
Parallel_reader::Ctx::traverse
Parallel_reader::Ctx::traverse_recs
2.2æ§è½æåææ
æä»¬ä½¿ç¨4U16Gè§æ ¼ECSå®ä¾ï¼é¨ç½²MySQL Community 8.0.14çæ¬ï¼innodb_buffer_pool_size设置为8GBãéç¨TPC-Hæµè¯æ¨¡åï¼Scale Factor(Gigabytes)为20ï¼lineitem表主é®å¤§å°çº¦17.4GBï¼äºçº§ç´¢å¼i_l_orderkey大å°çº¦2.3GBï¼äºçº§ç´¢å¼i_l_partkey_suppkey大å°çº¦3.3GBï¼è¡¨ç»æå¦ä¸ï¼
mysql> show create table lineitem\G
*************************** 1. row ***************************
Table: lineitemCreate Table:
CREATE TABLE `lineitem` (
`L_ORDERKEY` bigint NOT NULL,
`L_PARTKEY` int NOT NULL,
`L_SUPPKEY` int NOT NULL,
`L_LINENUMBER` int NOT NULL,
`L_QUANTITY` decimal(15,2) NOT NULL,
`L_EXTENDEDPRICE` decimal(15,2) NOT NULL,
`L_DISCOUNT` decimal(15,2) NOT NULL,
`L_TAX` decimal(15,2) NOT NULL,
`L_RETURNFLAG` char(1) NOT NULL,
`L_LINESTATUS` char(1) NOT NULL,
`L_SHIPDATE` date NOT NULL,
`L_COMMITDATE` date NOT NULL,
`L_RECEIPTDATE` date NOT NULL,
`L_SHIPINSTRUCT` char(25) NOT NULL,
`L_SHIPMODE` char(10) NOT NULL,
`L_COMMENT` varchar(44) NOT NULL,
PRIMARY KEY (`L_ORDERKEY`,`L_LINENUMBER`),
KEY `i_l_orderkey` (`L_ORDERKEY`),
KEY `i_l_partkey_suppkey` (`L_PARTKEY`,`L_SUPPKEY`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
lineitem表ç主é®çº¦17GBï¼æ æ³å ¨é¨å è½½å°Buffer Poolä¸ï¼æ¯æ¬¡COUNTæ§è¡è§¦åçç£çIOåºæ¬ç¸å(约82䏿¬¡)ãå¨è¿ä¸ªåºæ¯ä¸ï¼æåInnoDBå¹¶è¡æ«æå¹¶å度(innodb_parallel_read_threads)ï¼COUNTæ§è½å¯ä»¥çº¿æ§æåï¼1å¹¶åæ§è¡æ¶é´çº¦585ç§ï¼2å¹¶åæ§è¡æ¶é´çº¦300ç§ï¼4å¹¶åæ§è¡æ¶é´çº¦145ç§ï¼æ°æ®åè§å¾3ã
å¾3 MySQL 8.0 COUNTå¹¶è¡æåææ
2.3约æéå¶
(1) 社åºMySQL COUNTå¹¶è¡å¨InnoDBåå¨å¼æå®ç°ï¼åªæ¯æä¸»é®çå¹¶è¡æ«æï¼å¿½ç¥äºä¼åå¨éæ©çæä½³ç´¢å¼ãå½ä¸ä¸ªè¡¨ä¸»é®å¾å¤§ãäºçº§ç´¢å¼è¾å°ï¼ç¸æ¯èçæ¬(MySQL 5.7)ä¸²è¡æ«æäºçº§ç´¢å¼ï¼ç¤¾åºå¹¶è¡æ ä¼åææã
(2) 社åºMySQL COUNTå¹¶è¡åªæ¯ææ WHEREæ¡ä»¶çCOUNTï¼åå å¨äºInnoDBå卿 æ³è¿è¡è¿æ»¤è®¡ç®ã
(3) 彿«æä¸»é®æ°æ®éå¾å¤§æ¶ï¼å¯è½ä¼æ·æ±°Buffer Poolä¸ççæ°æ®ï¼å¯¼è´åç»çæ§è½æ³¢å¨ã
(4) 社åºMySQL COUNTå¹¶è¡å¼ºå¶çæï¼æ æ³å ³éï¼å½éå°(1)ä¸çæ§è½é®é¢æ¶ï¼æ æ³åéè³ä¸²è¡æ«æäºçº§ç´¢å¼ã
使ç¨2.2èç¸åçæµè¯ç¯å¢åæµè¯æ¨¡åï¼æ§è¡âSELECT COUNT(*) FROM lineitemâ SQLè¯å¥ï¼å¯¹æ¯MySQL 5.7.44çæ¬ä¸MySQL 8.0.14çæ¬æ§è¡æ¶é´ï¼æ°æ®åè§è¡¨1ã
表1 MySQL 5.7.44ä¸8.0.14çæ¬COUNTæ§è¡æ¶é´å¯¹æ¯
å¨è¿ä¸ªåºæ¯ä¸ï¼MySQL 8.0çæ¬ä½¿ç¨4å¹¶åæ«æä¸»é®ï¼ä½æ¯ç±äºæ«æçæ°æ®éè¾å¤§ï¼è§¦å大éçç£çIOï¼å¯¼è´æ§è½å·®äºMySQL 5.7ä¸²è¡æ«æäºçº§ç´¢å¼ã
3. GaussDB(for MySQL) COUNT ä¼å
é对MySQL COUNTå¹¶è¡åå¨çé®é¢ï¼GaussDB(for MySQL)è¿è¡äºé对æ§ä¼åï¼éè¿èªç çå¹¶è¡æ¥è¯¢(PQ)å计ç®ä¸æ¨(NDP)ç¹æ§ï¼å®ç°äºä¸å±å¹¶è¡ï¼å å¿«COUNTæ§è¡ãæ¡æ¶å¾åè§å¾4ã
- 第ä¸å±å¹¶è¡: SQL弿å±ï¼éè¿èªç å¹¶è¡æ¥è¯¢ï¼å©ç¨å¤æ ¸è®¡ç®å éï¼
- 第äºå±å¹¶è¡ï¼InnoDBåå¨å¼æå±ï¼éè¿èªç 计ç®ä¸æ¨ç¹æ§ï¼è§¦åæ¹é读请æ±ï¼SALå±å°æ¹é读çPageç»è£ ãæå ï¼å¹¶åå°è¯»è¯·æ±åéè³åå¸å¼åå¨(Page Store)ï¼
- 第ä¸å±å¹¶è¡ï¼Page Storeæ¥åå°è¯»è¯·æ±åï¼æ¯ä¸ªPage Storeå é¨å¹¶åååºè¯»è¯·æ±ï¼å¾ 页颿«æãè¿æ»¤ãèåæä½å®æåï¼å°ç»æè¿åè³è®¡ç®å±ã
å¾4 GaussDB(for MySQL) COUNTå¹¶è¡ä¼å
3.1åçä»ç»
ä¸é¢ä»ç»ä¸GaussDB(for MySQL) COUNTä¼åç»èã
3.1.1 æ¯æå¨æå ³é社åºMySQL COUNTå¹¶è¡
å½éå°2.3èçæ§è½é®é¢æ¶ï¼å¯ä»¥éè¿è°æ´åæ°âinnodb_parallel_select_countâå¨æå ³éæå¼å¯MySQL COUNTå¹¶è¡åè½ï¼ä½¿ç¨æ¹æ³å¦ä¸ï¼
mysql> SET innodb_parallel_select_count=OFF;mysql> EXPLAIN FORMAT=TREE SELECT COUNT(*) FROM lineitem\G
*************************** 1. row ***************************
EXPLAIN: -> Aggregate: count(0)
-> Index scan on lineitem using i_l_orderkey (cost=12902405.32 rows=118641035)
3.1.2 GaussDB(for MySQL)å¹¶è¡æ¥è¯¢ç¹æ§
GaussDB(for MySQL)æ¯æå¹¶è¡æ¥è¯¢(PQ)[1]ï¼ç¨ä»¥éä½åæåæ¥è¯¢åºæ¯çå¤çæ¶é´ï¼æ»¡è¶³ä¼ä¸çº§åºç¨å¯¹æ¥è¯¢ä½æ¶å»¶çè¦æ±ãç¸æ¯ç¤¾åºMySQLå¹¶è¡æ¥è¯¢ç诸å¤éå¶ï¼GaussDB(for MySQL)èªç çå¹¶è¡æ¥è¯¢æ¯æä¸»é®ãäºçº§ç´¢å¼å¤ç§æ«ææ¹å¼ï¼éç¨äºå¤§é¨åSELECTè¯å¥ã
é对COUNTæä½ï¼å¯ä»¥å©ç¨PQç¹æ§ï¼å¹¶è¡æ«æäºçº§ç´¢å¼ï¼æåæ¥è¯¢æ§è½ã
ç¨æ·å¯ä»¥éè¿Hintçæ¹å¼å¼å¯PQï¼å½æ§è¡è®¡åä¸åºç°ParallelãGatherå ³é®åæ¶ï¼è¯´æPQç¹æ§çæãä½¿ç¨æ¹æ³å¦ä¸ï¼
mysql> EXPLAIN FORMAT=TREE SELECT/*+ PQ() */ COUNT(*) FROM lineitem\G
*************************** 1. row ***************************
EXPLAIN: -> Aggregate: count(`<temporary>`.`0`)
-> Gather: 4 workers, parallel scan on lineitem
-> Aggregate: count(`<temporary>`.`0`)
-> Parallel index scan on lineitem using i_l_orderkey (cost=4004327.70 rows=29660259)
3.1.3 GaussDB(for MySQL)计ç®ä¸æ¨ç¹æ§
计ç®ä¸æ¨(Near Data Processing)[2]æ¯GaussDB(for MySQL)æé«æ°æ®å¤ææ¥è¯¢æççè§£å³æ¹æ¡ãéå¯¹æ°æ®å¯éåæ¥è¯¢ï¼å°åæå½±ãèåè¿ç®ãæ¡ä»¶è¿æ»¤çæä½ä»è®¡ç®èç¹å䏿¨éç»åå¸å¼åå¨å±çå¤ä¸ªèç¹ï¼å¹¶è¡æ§è¡ãéè¿è®¡ç®ä¸æ¨æ¹æ³ï¼æåäºå¹¶è¡å¤çè½åï¼åå°ç½ç»æµéå计ç®èç¹çååï¼æé«äºæ¥è¯¢å¤çæ§è¡æçã
é对COUNTæä½ï¼å¯ä»¥å©ç¨NDPç¹æ§ï¼å°èåæä½ä¸æ¨è³åå¸å¼åå¨ï¼åå°ç½ç»æµéï¼æåæ¥è¯¢æ§è½ã
ç¨æ·å¯ä»¥éè¿Hintçæ¹å¼å¼å¯NDPï¼æ§è¡è®¡åä¸åºç°NDP å ³é®åæ¶ï¼è¯´ææ¤ç¹æ§çæãä½¿ç¨æ¹æ³å¦ä¸ï¼
mysql> EXPLAIN FORMAT=TREE SELECT/*+ PQ() NDP_PUSHDOWN() */ COUNT(*) FROM lineitem\G
*************************** 1. row ***************************
EXPLAIN: -> Aggregate: count(`<temporary>`.`0`)
-> Gather: 4 workers, parallel scan on lineitem
-> Aggregate: count(`<temporary>`.`0`)
-> Parallel index scan on lineitem using i_l_orderkey Using pushed NDP (aggregate) (cost=4046562.45 rows=29047384)
3.2æ§è½ä¼åææ
使ç¨2.2èç¸åçæµè¯ç¯å¢åæµè¯æ¨¡åï¼æ§è¡âSELECT COUNT(*) FROM lineitemâ SQLè¯å¥ï¼å¯¹æ¯GaussDB(for MySQL)å¼å¯PQç¹æ§ä¸å¼å¯PQ+NDPç¹æ§çæ§è¡æ¶é´ï¼åè§è¡¨2ã
表2 GaussDB(for MySQL) COUNTæä½æ§è¡æ¶é´
仿µè¯ç»æçï¼åªå¼å¯PQç¹æ§ï¼å¹¶è¡æ¥è¯¢å¹¶å度设置为4ï¼ç£çIO约13䏿¬¡ï¼æ¥è¯¢èæ¶çº¦31ç§ï¼
åæ¶å¼å¯PQåNDPç¹æ§ï¼å¹¶è¡æ¥è¯¢å¹¶å度设置为4ï¼NDPéè¿IOåå¹¶å计ç®ä¸æ¨ï¼å¤§å¹ åå°äºç£çIOï¼æ¥è¯¢èæ¶åªæ1.7ç§ï¼ç¸æ¯ç¤¾åºMySQL 8.0.22 æ§è¡èæ¶145ç§ï¼COUNTæ§è½æåè¶ è¿80åã
å¾5 GaussDB(for MySQL) COUNTä¼åæåææ
4.æ»ç»
社åºMySQL 8.0å¼å ¥äºå¹¶è¡æ«æä¸»é®åè½ï¼ä½ä¸æ¯æå¹¶è¡æ«æäºçº§ç´¢å¼ï¼å¯¼è´å¨å¤§è¡¨æå·æ°æ®åºæ¯(è¡¨é¡µé¢æ°æ®ä¸å¨Buffer Pool)åèåºç°å£åï¼GaussDB(for MySQL)éè¿å¹¶è¡æ¥è¯¢(PQ)å计ç®ä¸æ¨(NDP)ç¹æ§ï¼è§£å³äºå¤§è¡¨COUNTæ ¢çé®é¢ï¼å ¸ååºæ¯ä¸ç¸æ¯ç¤¾åºå¹¶è¡ï¼æ§è½æåè¶ è¿80åï¼ä¸ºç¨æ·æä¾æ´å æè´çä½éªã
5.ç¸å ³åè
[1] å¹¶è¡æ¥è¯¢ï¼PQï¼https://support.huaweicloud.com/usermanual-gaussdbformysql/gaussdbformysql_05_0150.html
[2] ç®å䏿¨ï¼NDPï¼https://support.huaweicloud.com/usermanual-gaussdbformysql/gaussdbformysql_05_0129.html
å ³æ³¨#å为äºå¼åè èç# ç¹å»ä¸æ¹ï¼ç¬¬ä¸æ¶é´äºè§£åä¸ºäºæ°é²ææ¯~
å为äºå客_å¤§æ°æ®å客_AIå客_äºè®¡ç®å客_å¼åè ä¸å¿-å为äº