- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55582 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17626
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 447
- 主题
- 326
- 精华
- 1
- 分享
- 0
- 好友
- 79
TA的每日心情 | 慵懒 2020-7-12 09:52 |
|---|
签到天数: 116 天 [LV.6]常住居民II 管理员
 群组: 2018教师培训(呼和浩 群组: 2017-05-04 量化投资实 群组: 2017“草原杯”夏令营 群组: 2018美赛冲刺培训 群组: 2017 田老师国赛冲刺课 |
一:背景$ ]$ Q' I2 H$ _% f; X. R
1. 讲故事
4 z9 J. N/ ~2 Y最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
7 {& {9 L: T' y- b
, E2 S3 \% S* o5 r" W) o. Q' ]二:了解架构图0 O0 o7 g2 J* ]. z1 A
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。$ h) l; X4 n I/ c" m
3 k+ W& W7 D. L+ W' C1. 从架构图入手7 F. o* g' x* q1 F6 B' \
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。* {! T* G8 _ ^
/ b' Y2 D0 w* T% r$ t
; T3 D& w2 G4 N) m9 v4 [! n2 Z- t3 B9 `" ^5 o
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~* I9 W+ [1 N) V& f
: s7 z- T+ w# o
2. 功能点介绍
0 f" \, H8 }: A& ^1 jMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。0 H' u8 u, z t; ^2 A/ l! h1 P
+ N5 C1 O/ o+ e+ h# o% ^8 l<1> Client4 a& t7 }! B* @9 ]. W
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
2 N& f' o7 i# @! v; y
% D7 d }6 @% C& p& \ P/ t<2> Connection/Thread Pool5 |7 `0 H# r# a" f4 N
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。- A% E; `, C, a5 z$ b$ ~
8 N8 a* x' I7 C# P- S
<3> SqlInterface,Parse,Optimizer,Cache
8 h. ^$ h( M3 \2 r2 L对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。3 m" d) F' x5 z) b
6 E- v$ Y& Q1 K( i7 d' N& ^<4> Storage Engines
- L) }) l, {5 n负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
' j2 P; _0 q% B0 V4 W9 ]
* b# m( d7 M. b# w3 h三: 源码分析% b4 j% _% I$ I; V: h- B* i. \" J
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
- X5 U2 @8 d7 k" `2 M# l) w
W. g6 S4 B$ p& h1 e( l( m* U+ b* z1. 了解mysql是如何启动监听的
, X3 ?7 |* W, z* J+ M手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。; }7 a% ?' a% e+ E7 n8 A' n& T
![]()
, o6 Y; S2 k% {; f9 [& b. X
; G6 ?/ T0 M5 K! z' C
, ?6 X* N9 d- L# \$ Y# t9 E从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。7 e+ b k7 h1 l
' E7 s' w. h. |) @7 ~6 C
<1> mysqld_main 入口函数 => sql/main.cc
: F! ?+ m2 q% m- q4 k" X. _8 G6 A" A) U: u
: L6 `0 E: h# q8 B$ h/ bextern int mysqld_main(int argc, char **argv);
' y$ _! a2 I# q( n( I0 G5 n" P& L0 |) g- Y- _& S
int main(int argc, char **argv)$ f- n; }( a. r( P9 B. o
{ U0 t% n# {6 Y) S) r/ ]/ Y0 E
return mysqld_main(argc, argv);, t! A( r, v6 C" Q" ~* \; a9 D5 N# e4 M
}/ v% ~; s# ^6 L4 s, U, f
, x8 V! V/ a2 O/ T% q1 Z0 D% D( y1 \- W
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
. s* h, ~2 k4 u$ U+ g" I2 N6 \+ D$ e- q7 ]6 E. V
<2> 创建监听
" }) f4 F6 B# y, g0 T! M; ^% |* I# o0 r# K7 _6 c; _' l% Y
- m6 Q* J; L6 T$ A1 kint mysqld_main(int argc, char **argv) \8 ~( K8 ^2 H: A/ d5 X
{3 a0 B' Z- x2 B4 Y8 E0 I; ]
//创建服务监听线程
" ], _6 n2 X1 A/ U% ^# M: F handle_connections_sockets();% Z' z8 A. P0 Y& V* M
}2 W! H0 Y9 ~3 P4 H
/ x0 v) N1 k5 y4 L, b r- Gvoid handle_connections_sockets()' R. s n% S& ^2 F* A# N0 u2 {8 B
{
j/ z8 p1 t5 \% m& v //监听连接. L+ V4 a/ z: r: t6 E6 I$ T
new_sock= mysql_socket_accept(key_socket_client_connection, sock,8 |* o, u4 F, e% v
(struct sockaddr *)(&cAddr), &length);: c6 b" _. ]4 s
4 Z2 x$ @6 t+ A9 h( Y9 t7 }! @
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))' w9 k- C" j+ e. E) E, ^# Y+ T
thd->security_ctx->set_host((char*) my_localhost);$ }$ q: i+ g, b/ l/ `, U2 Z
" D* \( b5 V- }+ J //创建连接! f' U) E; [- z! n; M
create_new_thread(thd);
4 B! d" E- \2 E- i. T1 U3 u}2 ]6 ^( ?; |$ H0 X8 K6 s( f
- g0 W( R1 F( |7 J( y; Z4 V; O4 x4 }
//创建新线程处理处理用户连接2 _2 j5 k7 ^. r; ?
static void create_new_thread(THD *thd){$ Q; D. _% e; @4 ~0 _2 L9 _
2 ?, H& b( x ]2 P, |: U thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
3 f: v5 \ k1 ], [
3 \7 S. R4 p3 x" E //线程进了线程调度器
! |# H1 m9 d9 i/ N. t MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); - c! Q; Z* X% K: `. Q
}
( t- s0 `& t3 o Y* s2 c# a8 _2 L- V! E) L: j; V
6 B4 r0 J# g! |% `6 r) l
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
2 v" U* J, U" r$ x: C% U0 b
7 z F K0 X+ `- j5 N/ Y. I
* m) }1 q {. L9 W. ]& W4 F2. 理解mysql是如何处理sql请求
- h1 C" {9 I \6 B+ o H这里我以Insert操作为例稍微解剖下处理流程:
; x/ I+ ]# i3 z4 `, D$ M; O% s* R4 y/ g8 e* c* a* M3 J
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。7 D& L% R' d$ @; W$ e+ R! Q1 `
( g9 q, x" |0 X, `1 m8 h
7 k2 U4 t: u/ ^ Jstatic scheduler_functions one_thread_per_connection_scheduler_functions=" J* ?6 J, D& F1 O
{- [9 f- c: ~- \7 @0 R& [ C
0, // max_threads
9 v9 j! c7 L' y1 R3 n3 S NULL, // init8 b0 T& w% s+ y4 Z) Q6 c
init_new_connection_handler_thread, // init_new_connection_thread
# d/ D+ v5 O! x6 h% {" r8 I+ [& x create_thread_to_handle_connection, // add_connection# s* R7 ^; A; [" i# c' }5 X
NULL, // thd_wait_begin
3 m) Q" c: ^6 |. A) B- Z NULL, // thd_wait_end& q- ^% _5 L% \1 `
NULL, // post_kill_notification
# D7 U' r5 }9 B3 K( n one_thread_per_connection_end, // end_thread& i+ F* ~+ W' f
NULL, // end
% E6 ]' \ c- c( l) i+ u};
6 ], [: s' }4 O A" A1 v
( [4 a+ z( R7 w a0 L, A4 j* ?4 u( G5 x& u1 n
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
: w8 |6 k$ ^' Y+ o3 ~. `9 e. `
5 p6 H8 j' D, c& I5 n7 H<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪9 h/ E; x) e. u* n
6 t1 H5 }3 n9 Z: W% y8 i
void create_thread_to_handle_connection(THD *thd)
+ s$ e/ N% Q1 ^& T' S: e7 c# P{
. T+ y: |- M& l! g+ V1 z! V if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,9 {& `0 [9 k' l: |
handle_one_connection,(void*) thd))){}/ X) f1 d! J$ |9 h2 Q7 n
}
4 n, G$ T# S- P/ E4 j//触发回调函数 handle_one_connection o7 M) N$ g( Q( C
pthread_handler_t handle_one_connection(void *arg)
8 k x7 M: ~+ @. T2 T' T{
4 h0 k3 p; `: `5 l do_handle_one_connection(thd);
1 @( n% }4 K* e* k4 L e. P y5 P w}! B4 k7 k7 d6 }5 R
//继续处理+ k# I7 @# {6 ~2 D4 W" C
void do_handle_one_connection(THD *thd_arg){
9 q9 ^, {& H2 O+ {' W while (thd_is_connection_alive(thd))4 W! H6 ~5 a" K- g( x7 [- w
{
, S8 _9 D% e/ r mysql_audit_release(thd);
, }& ?' S, H) W$ R( {- I" W if (do_command(thd)) break; //这里的 do_command 继续处理
$ F4 E* }0 ~" g8 i/ a% | }
" J. P- c$ v1 M- v1 w}) }" `) D9 r. m1 p4 u; d. w! s- }) `( P
//继续分发6 c+ O1 K X) a, f/ k9 h
bool do_command(THD *thd)& H% T2 G' ~) f4 R
{
; O" y) P) \" }9 L. r( t' T" y return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
( _2 {: a" _. R" w2 h1 K}
+ g3 Z$ h1 \$ w9 U* Vbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
# c5 \7 a( Z2 t8 i9 E{
! [4 C* {1 K0 B/ W switch (command) {; _8 r( T2 a& P% g9 u% O& t6 m
case COM_INIT_DB: .... break;
' c9 p- O! X5 P& L. o ...* _' a# j' X: F. Y( y+ ?4 Z
case COM_QUERY: //查询语句: insert xxxx4 @- F0 {, W/ f R$ i! F% l
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
1 |/ z. ?! ?1 \: x$ l. Q: ^9 D: W break;% F2 F- c. P. d4 ^
}" b7 e; M" T( ~. E
}
$ `: r1 P* L$ k7 z//sql解析模块
- q ?& d6 ?) e) |2 ~; dvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)* b; j+ ?: M% F7 ?
{0 h5 y8 V' ]" t5 F4 F- y0 |) l
error= mysql_execute_command(thd);. i+ d' W- r, x
}
4 O2 }& u( A6 K1 z L0 V, a
7 S# ?5 l2 m4 i0 @" Y$ D5 v( |% Z/ `+ t# Z
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
/ `" Z$ }. h. w" }; O+ z0 z2 B
) L' E( d( G: Y* u//继续执行
( {, Z7 p4 C7 i. O, Q3 r7 sint mysql_execute_command(THD *thd)) C% F+ i& G# Y' h& ^
{
* b; v8 u2 r% X7 n* M9 G# A3 | switch (lex->sql_command)
5 ^- t' t' n5 Z {5 Y7 e! ?8 X7 m! S, t/ b6 P
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
) j% s$ Y# u7 S. ~2 u6 F) B/ r/ t$ Z$ T0 S- Y$ r4 N
//这个 insert 就是我要追的
H3 ]4 M1 h3 K& H" m1 Z case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,6 q* J( f. _* ^$ E3 o
lex->update_list, lex->value_list,0 | {; U* N C0 H5 u
lex->duplicates, lex->ignore);- g7 L+ @& ^9 g, k
}
) Z. J$ ]8 {# M" e}; k$ F) w' u/ d0 j) @
//insert插入操作处理8 l* Q( e3 Z! w+ P* c0 X2 `
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
/ I6 O3 [0 O3 H! [# {! z List<Item> &update_fields, List<Item> &update_values,
8 m" G% C/ X& r3 G enum_duplicates duplic, bool ignore); \$ t! m) S+ N- Q
{. `6 L5 _" b0 w& i0 {: B2 ^2 L
while ((values= its++))- t2 p7 w" {+ K- S* a% U
{
' j" B2 c; R5 d error= write_record(thd, table, &info, &update);
% a! T/ R) d. y3 C }
& J& L5 _+ t. T1 Z}
8 A& [ o( N3 e8 g//写入记录8 M7 J3 P0 M* _! C6 E
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
P1 O/ ]3 c# ?! V/ G{- S3 M( `# z0 ?- ~2 w; X) x2 j
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
1 p, {* x; Y. |1 S; r {* p1 T8 y. o- S/ v/ w1 G
// ha_write_row 重点是这个函数( s" N7 R+ H$ g8 J. Z R0 E
while ((error=table->file->ha_write_row(table->record[0]))) \# X# v. M# K! t0 b$ ?
{6 h' G. c5 x K# X& I: d# g
....1 G, L9 ~3 c7 S6 }8 H/ Z6 B& ?
}8 H/ e2 v9 k3 n6 z6 x W1 O
}
0 n4 x! E# f- `2 A, [" B) k& o( \}
$ y( l- F9 N# U+ g0 l9 |. [% \# v7 ^. K/ `& e
* ?! {( ^- F& X) _6 J. S
5 P% B0 f* y) I5 V; Q7 q% m- _" q6 ]可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。. @( {! B; [8 W7 ?$ E1 _$ Z7 C
( w8 A* w' W! u2 g) I4 g3 [/ Q
<3> 继续挖 ha_write_row
P: Q: G' x4 K X5 Y) z+ ]3 t& K+ Z( r( h5 w* q; C1 Z% ^8 q4 n
int handler::ha_write_row(uchar *buf)' n6 R( Z" B. _7 u" U+ w
{, f2 d7 E- Q/ M7 s. y
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
8 |1 F8 P! ]' F8 R1 z}8 l4 {# g* a; L8 r9 e7 s2 e
6 P% N. v4 j' j1 f+ d7 m//这是一个虚方法
8 f, d0 B+ C! x- u1 h, V4 V6 ivirtual int write_row(uchar *buf __attribute__((unused)))
" m* k' H* @& S( |8 t{
" |* ?* I/ p% K; h+ w& r: } return HA_ERR_WRONG_COMMAND;1 l8 @! S$ R" d8 H
}
1 z& j; i$ n; W1 ?( L, \- R
6 _: c. A( S) q+ c( R; w. y
, n, p, c I8 y9 M# \9 C/ O- X看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁) q/ n$ [; b6 }. b
2 N% Z! E: w# t9 |' n
3. 调用链图7 w$ v0 W/ \9 k; u
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。- w( x2 ~, X9 |7 H( D9 w
1 S* s& `6 F- N" V+ L
0 \3 N+ Y! J2 B$ N
5 D D0 a9 @3 t三:总结4 s3 S* l$ j$ V, y
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。' {& V' |( Q" I. v
————————————————
9 w- C8 S, h3 ]! X E+ `版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。. v3 d4 t8 I: L/ Q* N$ X9 c
原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874154 d C5 k, H( _
|
zan
|