SlideShare a Scribd company logo
MYSQL源碼
與資料庫規範
CNTV(央視國際網路) 資深MYSQL DBA
古雷
CMUG(中國MYSQL⽤用⼾戶組)成員
古雷 CNTV資深MYSQL⼯工程師
• 在MySQL DBA領域混跡多年
• 在搜狐和暢遊獲得成⾧長
• ⺫⽬目前在CNTV繼續深⼊入學習
MySQL
• 喜歡研究MySQL源碼
• 積極參加MySQL⽤用⼾戶組(CMUG)
的活動
• 由於愛好佛教,⼈人送綽號“古⼤大
師”——純屬朋友送的昵稱,希
望⼤大家⾒見怪不怪
可能涉及到的術語
• 資料庫——數據庫——database
• 資料——數據——data
• 欄位——字段——column, field
• 1位元——1位——one bit
• 1位元組——1個字節——one byte
• 檔案——⽂文件——file
• 暫存檔案——臨時⽂文件——temporary file
• 記憶體——內存——memory
• 字元——字符——character
• 網路──網絡──network
為什麼要看源碼
• 運維MySQL多年,⽤用得越多越想知道為什麼
• 看⽂文檔覺得不夠解渴
• 推⾏行資料庫開發規範,開發(研發)部⾨門的同事也想知道更多的“為什
麼”
• 進展:
• 了解了⼀一些執⾏行計劃的執⾏行過程
• 了解了InnoDB page的⼀一部分結構
• 了解了⼀一點有關SQL成本(cost)估算的內容
資料庫開發規範
• 學習、借鑒多家公司的規範
• 制訂並推⾏行適合⾃自⼰己公司的規範
• 每⼀一條規範都是不可打破的嗎?有適⽤用範圍嗎?
• 開發⼈人員可以被我說服嗎
• 嘗試向源碼尋求幫助
FILESORT
從⼀一個排序的SQL說起
• MySQL中,當ORDER BY無法使⽤用索引時,則產⽣生filesort
• 本例是⼀一個簡化的例⼦子,為了演⽰示filesort,有意不加索引
• 實際情況中,會有⼀一些稍微複雜的SQL不容易使⽤用索引,或者是確實缺
少索引
• 本例試圖以⼀一個簡化的例⼦子專⾨門展⽰示filesort的效果
5 古雷my sql源碼與資料庫規範
FILESORT
SELECT多⼀一個欄位,時間相差6倍
• select * 僅⽐比select id,name多了⼀一個欄位 address varchar(255)
• ⽽而且address全部是空,即NULL
• SELECT多⼀一個欄位,時間相差6倍——1.19秒VS 0.20秒
mysql>  flush  status;    
mysql>  select  *  from  testsort  order  by  name;    
mysql>  select  *  from  information_schema.session_status  where  variable_name  in  
('created_tmp_files','sort_merge_passes');    
+-------------------+----------------+    
|  VARIABLE_NAME          |  VARIABLE_VALUE  |    
+-------------------+----------------+    
|  CREATED_TMP_FILES  |  3                            |    
|  SORT_MERGE_PASSES  |  3                            |    
+-------------------+----------------+      
mysql>  flush  status;    
mysql>  select  id,name  from  testsort  order  by  name;    
mysql>  select  *  from  information_schema.session_status  where  variable_name  in  
('created_tmp_files','sort_merge_passes');    
+-------------------+----------------+    
|  VARIABLE_NAME          |  VARIABLE_VALUE  |    
+-------------------+----------------+    
|  CREATED_TMP_FILES  |  0                            |    
|  SORT_MERGE_PASSES  |  0                            |    
+-------------------+----------------+  
FILESORT排序記憶體計算
• select id,name,address from testsort order by name;
• order by name欄位name varchar(50) utf8:
• 50個utf8字元占50*3位元組,轉換為占2位元組編碼(150*2+2)/3=100
• name可為空,需要加1個位元組,共101位元組
• select id,name,address欄位
• id int占4位元組;name占50*3+1+1=152(兩個1的來源150<255,
151<255)
• address varchar(255): 255*3+2+2=769(兩個2的來源765>255,
767>255)
• name和address可以為空,共⽤用1個位元組表⽰示空值(各占⼀一bit)
• ⼀一個char*(指標),占8位元組(只存在於sort buffer中,在暫存檔案中不
存)
• 共101+4+152+769+1+8=1035位元組
⼀一⾏行之差
mysql>  set  sort_buffer_size=1035*122881;                flush  status;    
mysql>  select  *  into  outfile  '/data/dump/sort.txt'  from  testsort  order  by  name;    
Query  OK,  122881  rows  affected  (0.56  sec)  
mysql>  select  *  from  information_schema.session_status  where  variable_name  in  
('created_tmp_files','sort_merge_passes');    
+-------------------+----------------+    
|  VARIABLE_NAME          |  VARIABLE_VALUE  |    
+-------------------+----------------+    
|  CREATED_TMP_FILES  |  0                            |    
|  SORT_MERGE_PASSES  |  0                            |    
+-------------------+----------------+    
mysql>  set  sort_buffer_size=1035*122880;              flush  status;    
mysql>  select  *  into  outfile  '/data/dump/sort.txt'  from  testsort  order  by  name;    
Query  OK,  122881  rows  affected  (0.91  sec)  
mysql>  select  *  from  information_schema.session_status  where  variable_name  in  
('created_tmp_files','sort_merge_passes');    
+-------------------+----------------+    
|  VARIABLE_NAME          |  VARIABLE_VALUE  |    
+-------------------+----------------+    
|  CREATED_TMP_FILES  |  2                            |    
|  SORT_MERGE_PASSES  |  1                            |    
+-------------------+----------------+  
FILESORT兩種演算法的選擇
mysql>  set  max_length_for_sort_data=1024;  //讓filesort每行資料最大長度小於1035位元組  
mysql>  set  sort_buffer_size=117*122881;      //filesort會選擇另一演算法,每行只保存排序的  
mysql>  flush  status;                                                      //(接上面)  KEY、主鍵(8位元組)、指標(8位元組)  
mysql>  show  global  status  like  'innodb_rows_read';    
+------------------+---------+    
|  Variable_name        |  Value      |    
+------------------+---------+    
|  Innodb_rows_read  |  1720348  |    
+------------------+---------+    
mysql>  select  *  into  outfile  '/data/dump/sort.txt'  from  testsort  order  by  name;    
Query  OK,  122881  rows  affected  (0.66  sec)    
mysql>  select  *  from  information_schema.session_status  where  variable_name  in  
('created_tmp_files','sort_merge_passes','innodb_rows_read');    
+-------------------+----------------+    
|  VARIABLE_NAME          |  VARIABLE_VALUE  |    
+-------------------+----------------+    
|  CREATED_TMP_FILES  |  0                            |                                      //這次sort_buffer_size較小,也足夠用。  
|  INNODB_ROWS_READ    |  1966110                |                  //另外,1966110  -  1720348  =  245762  
|  SORT_MERGE_PASSES  |  0                            |                                  //(接上面)恰好是122881的兩倍  
+-------------------+----------------+  
FILESORT落實到資料庫規範
• 請勿使⽤用SELECT *
• SELECT後⾯面指明實際需要的欄位名,在⾜足夠⽤用的前提下,儘量少
• VARCHAR(n),n的⼤大⼩小也是需要計較的,在⾜足夠⽤用的前提下,儘量⼩小
• 把不會在SQL的WHERE以及ORDER BY、GROUP BY中出現的TEXT欄
位、⽐比較⼤大的VARCHAR欄位等單獨放在⼀一張表裡
• 上述習慣將避免很多後續的優化⼯工作
• ⽽而且有些優化⼯工作是成本很⾼高的,⽐比如把⼤大的欄位拆到新表裡
SORT_MERGE_PASSES的計算
mysql> desc tab;
+---------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(15) | YES | | NULL | |
| address | varchar(200) | YES | | NULL | |
+---------+--------------+------+-----+---------+----------------+
mysql> select * into outfile '/media/psf/Home/workspace/dump/sort.txt' from tab order by
name;
Query OK, 63584 rows affected (3.70 sec)
mysql> select * from information_schema.session_status where variable_name in
('created_tmp_files','sort_merge_passes');
+-------------------+----------------+
| VARIABLE_NAME | VARIABLE_VALUE |
+-------------------+----------------+
| CREATED_TMP_FILES | 3 |
| SORT_MERGE_PASSES | 28 |
+-------------------+----------------+
統計相關函數的執⾏行次數
引⼊入新玩具——SYSTEMTAP
root@ubuntu:~# stap calls_count.stp /usr/local/mysql56debug/bin/mysqld
make_sortkey 63584
write_keys 169
merge_buffers 28
create_temp_file 3
filesort_free_buffers 3
filesort 1
trace_filesort_information 1
init_for_filesort 1
merge_many_buff 1
merge_index 1
SYSTEMTAP腳本
root@ubuntu:~# cat calls_count.stp
global calls
probe process(@1).function("*write_keys*").call
, process(@1).function("*filesort*").call
, process(@1).function("*merge_buffers*").call
, process(@1).function("*create_temp_file*").call
, process(@1).function("*make_sortkey*").call
, process(@1).function("*merge_many_buff*").call
, process(@1).function(“*merge_index*").call {
calls[ppfunc()] ++
}
probe timer.s(10) {
foreach (name in calls- limit 30)
printf ("%st%dn", name, calls[name])
print("n")
}
SORT_MERGE_PASSES的計算
• 第1批合併:169=7*24+1;即合併24次排序結果,前23次每次合併7個,最後⼀一
次合併8個
• 第2批合併:24=7*3+3;即合併3次排序結果,前2次每次合併7個,最後⼀一次10
個
• 第3批合併:3個排序結果,最後再合併1次,得到最終結果
• 24+3+1=28;即調⽤用merge_buffers的次數,也即sort_merge_passes的值
函數 執⾏行次數 解釋
make_sortkey 65384 製作排序的key,本例是name欄位
write_keys 169
每在sort buffer中排⼀一次序,在排序後要把排序結果寫
⼊入暫存檔案,⼀一共寫了169次。也即排序排了169次
merge_buffers 28 合併排序結果,即進⾏行merge sort,每7個結果合併⼀一次
create_temp_file 3
創建了3個暫存檔案,其中⼀一個應該是
16*sort_buffer_size那麼⼤大
SYSTEMTAP監控暫存檔案的讀寫
• root@ubuntu:~# stap iotime.stp 19120 ##刪除了部分輸出內容
• (mysqld) access /tmp/MY2h0vCA read: 43682208 write: 43682208
• (mysqld) access /tmp/MYfjNKjs read: 87364416 write: 87364416
• (mysqld) access /tmp/MYXM65AV read: 41711104 write: 41711104
• (mysqld) access /media/psf/Home/workspace/dump/sort.txt read: 0
write: 36424243
• id int 4位元組,name varchar(15) utf8 45+1+1位元組,address
varchar(200) utf8 600+2+2,再加1個空值位元組,共656
• name作為排序的KEY,(45*2+2)/3=30,再加1個空值位元組,共31
• (656+31)*63584=43682208; 43682208*2=87364416
• 41711104/63584=656
• 169次排序,結果寫⼊入/tmp/MYfjNKjs;第⼀一批合併排序,結果寫⼊入/
tmp/MY2h0vCA;第⼆二批合併排序,結果寫⼊入/tmp/MYfjNKjs;第三批
合併排序,/tmp/MYXM65AV;最終輸出的結果從/tmp/MYXM65AV讀
SYSTEMTAP官網的腳本IOTIME.STP(⼀一)
root@ubuntu:~# cat iotime.stp
#! /usr/bin/env stap
global start
global time_io
function timestamp:long() { return gettimeofday_us() - start }
function proc:string() { return sprintf("%d (%s)", pid(), execname()) }
probe begin { start = gettimeofday_us() }
global filehandles, fileread, filewrite
probe syscall.open.return {
if( pid() == strtol(@1,10) ) {
filename = user_string($filename)
if ($return != -1) {
filehandles[pid(), $return] = filename
} else {
printf("%d %s access %s failn", timestamp(), proc(), filename)
}
}
}
IOTIME.STP(⼆二)
probe syscall.read.return
,syscall.pread.return { #MySQL使⽤用了pread
if( pid() == strtol(@1,10) ) {
p = pid()
fd = $fd
bytes = $return
time = gettimeofday_us() - @entry(gettimeofday_us())
if (bytes > 0)
fileread[p, fd] += bytes
time_io[p, fd] <<< time
}
}
IOTIME.STP(三)
probe syscall.write.return {
if( pid() == strtol(@1,10) ) {
p = pid()
fd = $fd
bytes = $return
time = gettimeofday_us() - @entry(gettimeofday_us())
if (bytes > 0)
filewrite[p, fd] += bytes
time_io[p, fd] <<< time
}
}
IOTIME.STP(四)
probe syscall.close {
if( pid() == strtol(@1,10) ) {
if ([pid(), $fd] in filehandles) {
printf("%d %s access %s read: %d write: %dn",
timestamp(), proc(), filehandles[pid(), $fd],
fileread[pid(), $fd], filewrite[pid(), $fd])
if (@count(time_io[pid(), $fd]))
printf("%d %s iotime %s time: %dn", timestamp(), proc(),
filehandles[pid(), $fd], @sum(time_io[pid(), $fd]))
}
delete fileread[pid(), $fd]
delete filewrite[pid(), $fd]
delete filehandles[pid(), $fd]
delete time_io[pid(),$fd]
}
}
總結
• 看MySQL源碼很有樂趣
• 可以很⾃自然地落實到資料庫開發規範上
• systemap好強⼤大,可以好好學習和利⽤用
• 當前正在學習:opitmizer_trace中cost和rows的計算公式
•
有什麼問題?交流⼀一下 謝謝⼤大家

More Related Content

PDF
MySQLとPostgreSQLにおける基本的なアカウント管理
PDF
MySQLとPostgreSQLの基本的なパラメータ比較
PDF
MySQL8.0 SYS スキーマ概要
PDF
MySQLとPostgreSQLの基本的なレプリケーション設定比較
PDF
MySQLとPostgreSQLの基本的なバックアップ比較
PDF
MySQLとPostgreSQLの基本的な実行プラン比較
PDF
MySQL Partition Engine
PDF
MySQL SYSスキーマのご紹介
MySQLとPostgreSQLにおける基本的なアカウント管理
MySQLとPostgreSQLの基本的なパラメータ比較
MySQL8.0 SYS スキーマ概要
MySQLとPostgreSQLの基本的なレプリケーション設定比較
MySQLとPostgreSQLの基本的なバックアップ比較
MySQLとPostgreSQLの基本的な実行プラン比較
MySQL Partition Engine
MySQL SYSスキーマのご紹介

What's hot (19)

PDF
AvailabilityZoneとHostAggregate
PPT
Maatkit で MySQL チューニング
PDF
PostgreSQL Unconference #26 No Error on PostgreSQL
PDF
PostgreSQL13 新機能紹介
PDF
PostgreSQL Unconference #29 Unicode IVS
PDF
Control distribution of virtual machines
PDF
ヤフー社内でやってるMySQLチューニングセミナー大公開
PPTX
OSC ver : MariaDB ColumnStore ベンチマークしちゃいませんか?
PDF
20140531 JPUGしくみ+アプリケーション分科会 勉強会資料
PDF
MySQLerの7つ道具 plus
PDF
DynamoDB MyNA・JPUG合同DB勉強会 in 東京
PDF
ヤフーのRDBと最新のMySQLの検証結果#yjdsw3
PDF
Migr8.rb チュートリアル
PDF
DBスキーマもバージョン管理したい!
KEY
カジュアルにソースコードリーディング
PPT
20090107 Postgre Sqlチューニング(Sql編)
PDF
Postgre sql9.3 newlockmode_and_etc
KEY
CMS時代のバックアップノウハウを身につけよう!
PPTX
HDFS (fsimage and edits) in CDH3,CDH4
AvailabilityZoneとHostAggregate
Maatkit で MySQL チューニング
PostgreSQL Unconference #26 No Error on PostgreSQL
PostgreSQL13 新機能紹介
PostgreSQL Unconference #29 Unicode IVS
Control distribution of virtual machines
ヤフー社内でやってるMySQLチューニングセミナー大公開
OSC ver : MariaDB ColumnStore ベンチマークしちゃいませんか?
20140531 JPUGしくみ+アプリケーション分科会 勉強会資料
MySQLerの7つ道具 plus
DynamoDB MyNA・JPUG合同DB勉強会 in 東京
ヤフーのRDBと最新のMySQLの検証結果#yjdsw3
Migr8.rb チュートリアル
DBスキーマもバージョン管理したい!
カジュアルにソースコードリーディング
20090107 Postgre Sqlチューニング(Sql編)
Postgre sql9.3 newlockmode_and_etc
CMS時代のバックアップノウハウを身につけよう!
HDFS (fsimage and edits) in CDH3,CDH4
Ad

Viewers also liked (6)

PDF
My sql overview 2012 04-25 by scott chen - 30min - tw-1
PPT
Social networking presentation 5 20
PDF
4 葉金榮-my sql優化 - 20151219
PDF
7 吕智超-ssd101
PPT
Company Snapshot
PPT
Social networking present 5 20
My sql overview 2012 04-25 by scott chen - 30min - tw-1
Social networking presentation 5 20
4 葉金榮-my sql優化 - 20151219
7 吕智超-ssd101
Company Snapshot
Social networking present 5 20
Ad

Similar to 5 古雷my sql源碼與資料庫規範 (7)

PDF
MySQL Casual LT : MySQL Upgrade 5.0 to 5.5
KEY
道具を磨くことのススメ
PDF
My sql casual12lt
PDF
MySQL 5.5 Update #denatech
PDF
データベース03 - SQL(CREATE, INSERT, DELETE, UPDATEなど)
PPT
Online schema change_for_mysql_casual
PPTX
MySQL clients
MySQL Casual LT : MySQL Upgrade 5.0 to 5.5
道具を磨くことのススメ
My sql casual12lt
MySQL 5.5 Update #denatech
データベース03 - SQL(CREATE, INSERT, DELETE, UPDATEなど)
Online schema change_for_mysql_casual
MySQL clients

More from Ivan Tu (10)

PDF
8 彭立勳-double binlog方案
PDF
3 周彦偉-隨需而變 我所經歷的my sql架構變遷﹣周彥偉﹣acmug@2015.12台北
PDF
2 ivan ma-mysql複製的演進和應用-twn- v1
PDF
1 my sql20151219-kaji_ivan
PDF
My sql fabric webinar tw2
PDF
My sql resources_april2012_zht
PDF
My sql 56_roadmap_april2012_zht2
PDF
My sql cluster_taipei_event
PDF
My sql introduction for Bestcom
PDF
My S Q L Introduction for 1 day training
8 彭立勳-double binlog方案
3 周彦偉-隨需而變 我所經歷的my sql架構變遷﹣周彥偉﹣acmug@2015.12台北
2 ivan ma-mysql複製的演進和應用-twn- v1
1 my sql20151219-kaji_ivan
My sql fabric webinar tw2
My sql resources_april2012_zht
My sql 56_roadmap_april2012_zht2
My sql cluster_taipei_event
My sql introduction for Bestcom
My S Q L Introduction for 1 day training

5 古雷my sql源碼與資料庫規範

  • 2. CMUG(中國MYSQL⽤用⼾戶組)成員 古雷 CNTV資深MYSQL⼯工程師 • 在MySQL DBA領域混跡多年 • 在搜狐和暢遊獲得成⾧長 • ⺫⽬目前在CNTV繼續深⼊入學習 MySQL • 喜歡研究MySQL源碼 • 積極參加MySQL⽤用⼾戶組(CMUG) 的活動 • 由於愛好佛教,⼈人送綽號“古⼤大 師”——純屬朋友送的昵稱,希 望⼤大家⾒見怪不怪
  • 3. 可能涉及到的術語 • 資料庫——數據庫——database • 資料——數據——data • 欄位——字段——column, field • 1位元——1位——one bit • 1位元組——1個字節——one byte • 檔案——⽂文件——file • 暫存檔案——臨時⽂文件——temporary file • 記憶體——內存——memory • 字元——字符——character • 網路──網絡──network
  • 4. 為什麼要看源碼 • 運維MySQL多年,⽤用得越多越想知道為什麼 • 看⽂文檔覺得不夠解渴 • 推⾏行資料庫開發規範,開發(研發)部⾨門的同事也想知道更多的“為什 麼” • 進展: • 了解了⼀一些執⾏行計劃的執⾏行過程 • 了解了InnoDB page的⼀一部分結構 • 了解了⼀一點有關SQL成本(cost)估算的內容
  • 5. 資料庫開發規範 • 學習、借鑒多家公司的規範 • 制訂並推⾏行適合⾃自⼰己公司的規範 • 每⼀一條規範都是不可打破的嗎?有適⽤用範圍嗎? • 開發⼈人員可以被我說服嗎 • 嘗試向源碼尋求幫助
  • 6. FILESORT 從⼀一個排序的SQL說起 • MySQL中,當ORDER BY無法使⽤用索引時,則產⽣生filesort • 本例是⼀一個簡化的例⼦子,為了演⽰示filesort,有意不加索引 • 實際情況中,會有⼀一些稍微複雜的SQL不容易使⽤用索引,或者是確實缺 少索引 • 本例試圖以⼀一個簡化的例⼦子專⾨門展⽰示filesort的效果
  • 8. FILESORT SELECT多⼀一個欄位,時間相差6倍 • select * 僅⽐比select id,name多了⼀一個欄位 address varchar(255) • ⽽而且address全部是空,即NULL • SELECT多⼀一個欄位,時間相差6倍——1.19秒VS 0.20秒
  • 9. mysql>  flush  status;     mysql>  select  *  from  testsort  order  by  name;     mysql>  select  *  from  information_schema.session_status  where  variable_name  in   ('created_tmp_files','sort_merge_passes');     +-------------------+----------------+     |  VARIABLE_NAME          |  VARIABLE_VALUE  |     +-------------------+----------------+     |  CREATED_TMP_FILES  |  3                            |     |  SORT_MERGE_PASSES  |  3                            |     +-------------------+----------------+       mysql>  flush  status;     mysql>  select  id,name  from  testsort  order  by  name;     mysql>  select  *  from  information_schema.session_status  where  variable_name  in   ('created_tmp_files','sort_merge_passes');     +-------------------+----------------+     |  VARIABLE_NAME          |  VARIABLE_VALUE  |     +-------------------+----------------+     |  CREATED_TMP_FILES  |  0                            |     |  SORT_MERGE_PASSES  |  0                            |     +-------------------+----------------+  
  • 10. FILESORT排序記憶體計算 • select id,name,address from testsort order by name; • order by name欄位name varchar(50) utf8: • 50個utf8字元占50*3位元組,轉換為占2位元組編碼(150*2+2)/3=100 • name可為空,需要加1個位元組,共101位元組 • select id,name,address欄位 • id int占4位元組;name占50*3+1+1=152(兩個1的來源150<255, 151<255) • address varchar(255): 255*3+2+2=769(兩個2的來源765>255, 767>255) • name和address可以為空,共⽤用1個位元組表⽰示空值(各占⼀一bit) • ⼀一個char*(指標),占8位元組(只存在於sort buffer中,在暫存檔案中不 存) • 共101+4+152+769+1+8=1035位元組
  • 11. ⼀一⾏行之差 mysql>  set  sort_buffer_size=1035*122881;                flush  status;     mysql>  select  *  into  outfile  '/data/dump/sort.txt'  from  testsort  order  by  name;     Query  OK,  122881  rows  affected  (0.56  sec)   mysql>  select  *  from  information_schema.session_status  where  variable_name  in   ('created_tmp_files','sort_merge_passes');     +-------------------+----------------+     |  VARIABLE_NAME          |  VARIABLE_VALUE  |     +-------------------+----------------+     |  CREATED_TMP_FILES  |  0                            |     |  SORT_MERGE_PASSES  |  0                            |     +-------------------+----------------+     mysql>  set  sort_buffer_size=1035*122880;              flush  status;     mysql>  select  *  into  outfile  '/data/dump/sort.txt'  from  testsort  order  by  name;     Query  OK,  122881  rows  affected  (0.91  sec)   mysql>  select  *  from  information_schema.session_status  where  variable_name  in   ('created_tmp_files','sort_merge_passes');     +-------------------+----------------+     |  VARIABLE_NAME          |  VARIABLE_VALUE  |     +-------------------+----------------+     |  CREATED_TMP_FILES  |  2                            |     |  SORT_MERGE_PASSES  |  1                            |     +-------------------+----------------+  
  • 12. FILESORT兩種演算法的選擇 mysql>  set  max_length_for_sort_data=1024;  //讓filesort每行資料最大長度小於1035位元組   mysql>  set  sort_buffer_size=117*122881;      //filesort會選擇另一演算法,每行只保存排序的   mysql>  flush  status;                                                      //(接上面)  KEY、主鍵(8位元組)、指標(8位元組)   mysql>  show  global  status  like  'innodb_rows_read';     +------------------+---------+     |  Variable_name        |  Value      |     +------------------+---------+     |  Innodb_rows_read  |  1720348  |     +------------------+---------+     mysql>  select  *  into  outfile  '/data/dump/sort.txt'  from  testsort  order  by  name;     Query  OK,  122881  rows  affected  (0.66  sec)     mysql>  select  *  from  information_schema.session_status  where  variable_name  in   ('created_tmp_files','sort_merge_passes','innodb_rows_read');     +-------------------+----------------+     |  VARIABLE_NAME          |  VARIABLE_VALUE  |     +-------------------+----------------+     |  CREATED_TMP_FILES  |  0                            |                                      //這次sort_buffer_size較小,也足夠用。   |  INNODB_ROWS_READ    |  1966110                |                  //另外,1966110  -  1720348  =  245762   |  SORT_MERGE_PASSES  |  0                            |                                  //(接上面)恰好是122881的兩倍   +-------------------+----------------+  
  • 13. FILESORT落實到資料庫規範 • 請勿使⽤用SELECT * • SELECT後⾯面指明實際需要的欄位名,在⾜足夠⽤用的前提下,儘量少 • VARCHAR(n),n的⼤大⼩小也是需要計較的,在⾜足夠⽤用的前提下,儘量⼩小 • 把不會在SQL的WHERE以及ORDER BY、GROUP BY中出現的TEXT欄 位、⽐比較⼤大的VARCHAR欄位等單獨放在⼀一張表裡 • 上述習慣將避免很多後續的優化⼯工作 • ⽽而且有些優化⼯工作是成本很⾼高的,⽐比如把⼤大的欄位拆到新表裡
  • 14. SORT_MERGE_PASSES的計算 mysql> desc tab; +---------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(15) | YES | | NULL | | | address | varchar(200) | YES | | NULL | | +---------+--------------+------+-----+---------+----------------+ mysql> select * into outfile '/media/psf/Home/workspace/dump/sort.txt' from tab order by name; Query OK, 63584 rows affected (3.70 sec) mysql> select * from information_schema.session_status where variable_name in ('created_tmp_files','sort_merge_passes'); +-------------------+----------------+ | VARIABLE_NAME | VARIABLE_VALUE | +-------------------+----------------+ | CREATED_TMP_FILES | 3 | | SORT_MERGE_PASSES | 28 | +-------------------+----------------+
  • 15. 統計相關函數的執⾏行次數 引⼊入新玩具——SYSTEMTAP root@ubuntu:~# stap calls_count.stp /usr/local/mysql56debug/bin/mysqld make_sortkey 63584 write_keys 169 merge_buffers 28 create_temp_file 3 filesort_free_buffers 3 filesort 1 trace_filesort_information 1 init_for_filesort 1 merge_many_buff 1 merge_index 1
  • 16. SYSTEMTAP腳本 root@ubuntu:~# cat calls_count.stp global calls probe process(@1).function("*write_keys*").call , process(@1).function("*filesort*").call , process(@1).function("*merge_buffers*").call , process(@1).function("*create_temp_file*").call , process(@1).function("*make_sortkey*").call , process(@1).function("*merge_many_buff*").call , process(@1).function(“*merge_index*").call { calls[ppfunc()] ++ } probe timer.s(10) { foreach (name in calls- limit 30) printf ("%st%dn", name, calls[name]) print("n") }
  • 17. SORT_MERGE_PASSES的計算 • 第1批合併:169=7*24+1;即合併24次排序結果,前23次每次合併7個,最後⼀一 次合併8個 • 第2批合併:24=7*3+3;即合併3次排序結果,前2次每次合併7個,最後⼀一次10 個 • 第3批合併:3個排序結果,最後再合併1次,得到最終結果 • 24+3+1=28;即調⽤用merge_buffers的次數,也即sort_merge_passes的值 函數 執⾏行次數 解釋 make_sortkey 65384 製作排序的key,本例是name欄位 write_keys 169 每在sort buffer中排⼀一次序,在排序後要把排序結果寫 ⼊入暫存檔案,⼀一共寫了169次。也即排序排了169次 merge_buffers 28 合併排序結果,即進⾏行merge sort,每7個結果合併⼀一次 create_temp_file 3 創建了3個暫存檔案,其中⼀一個應該是 16*sort_buffer_size那麼⼤大
  • 18. SYSTEMTAP監控暫存檔案的讀寫 • root@ubuntu:~# stap iotime.stp 19120 ##刪除了部分輸出內容 • (mysqld) access /tmp/MY2h0vCA read: 43682208 write: 43682208 • (mysqld) access /tmp/MYfjNKjs read: 87364416 write: 87364416 • (mysqld) access /tmp/MYXM65AV read: 41711104 write: 41711104 • (mysqld) access /media/psf/Home/workspace/dump/sort.txt read: 0 write: 36424243 • id int 4位元組,name varchar(15) utf8 45+1+1位元組,address varchar(200) utf8 600+2+2,再加1個空值位元組,共656 • name作為排序的KEY,(45*2+2)/3=30,再加1個空值位元組,共31 • (656+31)*63584=43682208; 43682208*2=87364416 • 41711104/63584=656 • 169次排序,結果寫⼊入/tmp/MYfjNKjs;第⼀一批合併排序,結果寫⼊入/ tmp/MY2h0vCA;第⼆二批合併排序,結果寫⼊入/tmp/MYfjNKjs;第三批 合併排序,/tmp/MYXM65AV;最終輸出的結果從/tmp/MYXM65AV讀
  • 19. SYSTEMTAP官網的腳本IOTIME.STP(⼀一) root@ubuntu:~# cat iotime.stp #! /usr/bin/env stap global start global time_io function timestamp:long() { return gettimeofday_us() - start } function proc:string() { return sprintf("%d (%s)", pid(), execname()) } probe begin { start = gettimeofday_us() } global filehandles, fileread, filewrite probe syscall.open.return { if( pid() == strtol(@1,10) ) { filename = user_string($filename) if ($return != -1) { filehandles[pid(), $return] = filename } else { printf("%d %s access %s failn", timestamp(), proc(), filename) } } }
  • 20. IOTIME.STP(⼆二) probe syscall.read.return ,syscall.pread.return { #MySQL使⽤用了pread if( pid() == strtol(@1,10) ) { p = pid() fd = $fd bytes = $return time = gettimeofday_us() - @entry(gettimeofday_us()) if (bytes > 0) fileread[p, fd] += bytes time_io[p, fd] <<< time } }
  • 21. IOTIME.STP(三) probe syscall.write.return { if( pid() == strtol(@1,10) ) { p = pid() fd = $fd bytes = $return time = gettimeofday_us() - @entry(gettimeofday_us()) if (bytes > 0) filewrite[p, fd] += bytes time_io[p, fd] <<< time } }
  • 22. IOTIME.STP(四) probe syscall.close { if( pid() == strtol(@1,10) ) { if ([pid(), $fd] in filehandles) { printf("%d %s access %s read: %d write: %dn", timestamp(), proc(), filehandles[pid(), $fd], fileread[pid(), $fd], filewrite[pid(), $fd]) if (@count(time_io[pid(), $fd])) printf("%d %s iotime %s time: %dn", timestamp(), proc(), filehandles[pid(), $fd], @sum(time_io[pid(), $fd])) } delete fileread[pid(), $fd] delete filewrite[pid(), $fd] delete filehandles[pid(), $fd] delete time_io[pid(),$fd] } }
  • 23. 總結 • 看MySQL源碼很有樂趣 • 可以很⾃自然地落實到資料庫開發規範上 • systemap好強⼤大,可以好好學習和利⽤用 • 當前正在學習:opitmizer_trace中cost和rows的計算公式 • 有什麼問題?交流⼀一下 謝謝⼤大家