- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55583 点
- 威望
- 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 田老师国赛冲刺课 |
一:背景
; k6 |8 G3 H {2 k1. 讲故事
3 }% ]1 b' ^; C% V9 D最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。5 b" L* z) m f3 R
" H% k2 Y' s6 { s二:了解架构图1 x/ g' y* M/ ]7 q
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
A/ V4 M( v$ v6 u2 S- ?8 h1 A8 J. j" ]1 l$ z# h
1. 从架构图入手& T3 x, v3 z0 F. d2 k" `. s
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
/ z; t: L' m1 f: r2 C5 d![]()
! R2 F. C: t% D' o- w% |6 \8 e5 b0 A9 `6 Z5 m
" J: E! r$ i8 y9 v! Y$ N其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
0 V- h V9 `6 B3 P3 P5 Q
6 T$ {4 w8 s& t( U6 T( `2. 功能点介绍
" y7 L& {& {+ x: w8 MMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
/ X3 y3 `* p M+ S1 ~2 |0 H$ v2 t1 }, E0 S+ m1 Z( f `: z
<1> Client, I: P+ i& d0 C
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
1 E$ a7 M/ x' T2 w2 b* H
# e, l$ ~5 K: y' s' j. c! b<2> Connection/Thread Pool
, |# g/ ~% ~8 G" m; \MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。) m" @7 t7 p* r0 Y+ _, R& T
[0 K* [. B# W+ I# F7 i<3> SqlInterface,Parse,Optimizer,Cache
* u4 }7 m+ V4 N# G4 I0 F4 z对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
& P. V0 W% F. I, [
# c) |/ h- [( B# v5 h<4> Storage Engines
& k* {# ^& Q9 x4 w% F负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。" k; _5 ]6 C7 V9 Z
; C8 v. V2 x! ~+ s6 W
三: 源码分析# v b* g( ?/ G- t' @1 O
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
% D# ~9 n* h4 G; L8 V! `3 i, }2 H u ~
. b3 z( q! y, I' {! R2 D* u* x1. 了解mysql是如何启动监听的% a5 C$ i8 x3 N6 E5 X j$ N$ R
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
/ O# e: `/ @9 p# [" ~9 N+ z) r% ~![]()
$ l6 s3 u* w5 n% |' T
2 K/ Q6 [- R2 F3 E8 ^) N2 K! Z8 [% b! B8 `% t% t
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
3 y. Y. y7 q; W$ V+ ], |. ~
# w/ O- y. ]% |5 Z8 `<1> mysqld_main 入口函数 => sql/main.cc) f& C- r" S2 j% G7 }
1 Z1 y7 M; K2 l; M. d( \2 O% w
; G3 u( k2 z8 Y1 u5 e5 }0 b7 s
extern int mysqld_main(int argc, char **argv);: ]3 G' t0 X- K8 b3 l( O' M1 z
/ ^) I/ U) f: S: ^
int main(int argc, char **argv)
5 c' V6 U" N' q) `2 [{
" b* N. D+ F6 M+ ] return mysqld_main(argc, argv);' t. `8 {7 q0 H( V: p( T
}
3 G! {7 p) E! d, W/ Q0 z C% q
+ X) H- p0 o2 z& S, g
7 O2 r. {+ _* L/ k. K0 D% ?, G: Y这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。1 j& [4 F% I C, M. ` k
& F4 P" R5 q2 `4 z$ G: |% E0 R<2> 创建监听
0 T3 E! n/ ^6 c5 \# k
* z$ G: O! x% t" w) I) y8 U m* t1 q$ G3 {$ U J
int mysqld_main(int argc, char **argv)( x" n' U4 j3 U5 a" {. p7 M
{& U) \8 Z; E8 ] [; P% a7 P) [
//创建服务监听线程1 b p) ]2 z6 v; v+ l8 n) ]
handle_connections_sockets();9 G, V( m8 h! M8 ?
}" T7 ^8 s4 i. u8 O- a! t
# _9 n1 i* P a
void handle_connections_sockets()1 M9 i; b- O& b4 T! X
{8 U% [* q2 y) W: g6 m. c! b
//监听连接5 n+ v; j; t% x& A7 s
new_sock= mysql_socket_accept(key_socket_client_connection, sock,; U7 c! @- q: q- v0 R! |
(struct sockaddr *)(&cAddr), &length);
# P! V+ [8 u4 J/ v: Y' q% }
6 j: r( i( v! j; q9 R* k if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
2 G% `; {+ v7 u$ e! Y thd->security_ctx->set_host((char*) my_localhost);
4 p8 B7 B; ` ]& N& C' T4 X
D1 t1 t C( R1 G //创建连接
4 z3 W* t, C$ H7 k7 K" s% Q& z# i create_new_thread(thd);2 m. ]& p, \+ ~' J
}
- u) a' I1 [) a) S; s5 k+ L' n+ h2 _0 ?% D S
//创建新线程处理处理用户连接& i* c2 X% _+ R# ?9 v" M7 g
static void create_new_thread(THD *thd){
, {" j3 n4 Q8 H: A0 ]# X( H- p: b9 q) h! n! D
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;% ^% g) c5 e# ^4 W" C
) q6 ]/ O5 T) F
//线程进了线程调度器
8 z. D% K3 K5 ^$ E! z MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
' @6 H; \! r1 R' q' Q. _}
( j7 K9 ^9 R4 X1 w0 y
' H& F1 W# h0 Q+ @
' I, Q) B. `; `4 k' T, ?) @至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。0 V. [8 Y1 j( k, o! R4 D
$ o) @3 T1 W6 o/ M3 y6 s
! ?. Q0 G8 N3 V, [% D7 v" }
2. 理解mysql是如何处理sql请求% g, n+ `$ N1 v, v
这里我以Insert操作为例稍微解剖下处理流程:
6 E; v& q% B0 |% i, W" E. s
/ U9 p1 p$ S8 a/ k- T; M8 b当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
7 ~6 \0 G1 K" Z. o( ~- @# L, |; F; F2 `
5 t- j4 X- d; B! Z: m: K4 t
static scheduler_functions one_thread_per_connection_scheduler_functions=
5 V+ W% n, X$ f* e{3 Z' U/ i; S+ l
0, // max_threads
0 h/ d3 c: g8 W i5 \) A e NULL, // init7 B$ B9 @, _# ~' ]! v7 d/ r
init_new_connection_handler_thread, // init_new_connection_thread
5 |* J/ H( {8 A create_thread_to_handle_connection, // add_connection
, K9 ]9 j$ K0 U NULL, // thd_wait_begin% `$ G% h Z5 l' r r9 ^
NULL, // thd_wait_end
6 T4 [! V8 v9 W" |" P; s% B NULL, // post_kill_notification& Q6 N' U3 A; a: q
one_thread_per_connection_end, // end_thread" L8 @7 v$ p* [3 `7 H) Q1 j1 @
NULL, // end. ~0 |% v, y' j+ v1 l4 i
};$ ? P2 U9 x+ |7 S+ ]8 r3 M
% V' f' P' ^" m- B* J0 m
% p, O3 N) M+ e. ?
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
% S0 X- p! Q4 m+ L7 s" A _3 n$ u1 C2 }, t# a
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪# n+ ?" H6 D y) r& K- W& c. R. Z
, V% v3 D# i8 i# w# f) a" V8 fvoid create_thread_to_handle_connection(THD *thd)6 p/ V [8 h8 ]. W! X
{# z6 e9 T1 K3 L8 d
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
. S6 E4 a+ w" w handle_one_connection,(void*) thd))){}5 p' B( }. O, ]4 h s, P: |
}
1 C3 ~/ K; x, W& Q R//触发回调函数 handle_one_connection
( k; _6 ]. D9 ]# Gpthread_handler_t handle_one_connection(void *arg)
* I2 m R% t* I9 f{: `! |! I; i7 `: t" n6 J
do_handle_one_connection(thd);8 x7 h }; ]0 U- ?+ A" j
}
6 l! Y9 }! z& `) w& H3 w5 C! r//继续处理
# M; \, c; N5 Xvoid do_handle_one_connection(THD *thd_arg){" a$ a; o! }& ?& R1 \* f% z
while (thd_is_connection_alive(thd))
9 L9 v! G. G' X- J {+ e1 ^9 y8 z! }( H
mysql_audit_release(thd);
) f2 o- o0 g: U& r! F) c# z5 p5 C if (do_command(thd)) break; //这里的 do_command 继续处理
8 ~. \/ S H$ a9 ]1 M6 A* ?- t7 W }+ u! e+ z; I1 u+ @6 d" c$ N2 q# j
}. |$ f4 S( ~3 G" t8 Y" N
//继续分发
) n. p& _0 p: p2 s3 V0 D' E b7 zbool do_command(THD *thd)
6 `' q) N) v- j* p" r! _ h! p5 L{
/ ^0 a% H6 x' B0 |& e; O) A' @ return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));8 v1 v5 a L: R% _$ @
}! J$ V# W7 j" ?- e* V4 t) n
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
3 o4 y1 @9 H! Y+ p. ?{% I+ K; i1 z) F1 x
switch (command) {& J2 I4 j1 a& H% k
case COM_INIT_DB: .... break;* N2 R& M- |4 G* t5 w8 H& i
...
, V7 \+ P/ X) [! D4 z. s) t case COM_QUERY: //查询语句: insert xxxx
0 `1 n+ W0 s0 G& R- f1 a* C mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析( Y3 E5 a( Q% H0 `9 F
break;
: z! E4 A: B5 s% R8 |4 I }
) |5 v S* q. w* L4 c: a, y$ |$ e}
( ~7 c9 ^, k& Y$ U; G6 l. Q//sql解析模块
2 e; p- ]5 v2 ^! w. r/ y$ ^void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
$ s* S {+ a3 E; V{
- `9 V, Q0 T9 E* e3 z error= mysql_execute_command(thd);
- K0 Z! h% l; r$ ^* |" L}
5 G/ j% Z, G7 I1 O0 N9 ^* k: x6 L3 M5 m. D! _; V2 O) {$ F, u
* I, r( Q% _2 D1 `
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
3 |8 n% N/ n, J+ t0 Z) r. d0 |. Y2 f- g( Q
//继续执行4 S, o& B6 F$ p7 R2 }' l0 Y- _
int mysql_execute_command(THD *thd)
% Q" e7 Q/ o( j1 D* d# E{- @( e5 Y! P1 n0 m$ l
switch (lex->sql_command)
3 h: ` d9 N' } {7 a5 j) S) d" C: w- @: X
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;! C9 j6 [* N( c# U4 Q& F- z
! H/ ~- i4 S) h. P' X //这个 insert 就是我要追的
0 W7 C5 v3 K# _7 X6 G case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,: O0 z, u8 p2 I. g! k
lex->update_list, lex->value_list,' \! }; X: A1 o
lex->duplicates, lex->ignore);; i. P0 O" C0 I3 u- l. u
}; }7 |- r0 ^3 t6 i
}
3 J! k! E/ M# d//insert插入操作处理
% n) `$ n& Z# d. V& Pbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
3 | z4 r* r/ ^ List<Item> &update_fields, List<Item> &update_values, . u* F0 N( Q" n! ] S& U" z3 ~. }0 z
enum_duplicates duplic, bool ignore)) T- y3 q) f& a) s8 l/ q
{
m$ O2 Z9 r- Z; V$ P: x( x8 W while ((values= its++))
6 G9 C0 C8 Q( P" a$ W {% u3 E- v q& w& l
error= write_record(thd, table, &info, &update);& y0 `# U& j& x& B& b) K
}
" I3 ~; h8 n, g: m6 N. C}
, w; d% e3 `+ p% S, ?: S/ j. S6 D//写入记录
1 H. u& P" X/ c/ }8 R& q5 Rint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)- U' j, F5 j" u g
{
3 f" m. H( i. l; Q6 ^ if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)# _& l, m, P- ~. Z8 m% H) a, c5 t9 x
{
; e+ s; _5 L O( V // ha_write_row 重点是这个函数4 w1 G9 V, H! |7 _4 t0 v% M2 h
while ((error=table->file->ha_write_row(table->record[0])))
J4 \- i! f) T4 P; l' N5 y {! [* O' x2 f5 R4 g6 `% B" n
....( Z; \1 V) `8 w$ j
}! M. ^& R7 n- E1 J% C& _: |
}
( h) {+ i) C, ]}
& O. V. [8 V" B$ G( U/ a4 G) S6 f' P1 i+ d/ n
0 G% H, S) E, \. L0 s+ U' O0 l- A; C# |9 Y }# G
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
$ } M4 x7 o6 a5 ]& |
, k* E& K8 _. ~/ k+ G$ t<3> 继续挖 ha_write_row
, g* j" k _1 @* d6 M
4 J& h5 {' W7 R" k" H4 Eint handler::ha_write_row(uchar *buf)+ }4 _- E( \6 X, U `$ s! t4 b7 |6 H
{( f: m" J; `: C7 v9 p+ B
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })- O2 j0 l$ ^: O* z: L
}
) h( Z9 z6 @& X. w3 d1 f3 Y
- m8 }( n9 `" d4 y! a0 C4 y. H//这是一个虚方法
- ~! ?" j4 k+ Z0 d/ Nvirtual int write_row(uchar *buf __attribute__((unused)))
3 \( h$ L; U5 E! q b{" z* j% }, ?6 V6 p+ Q# r; O
return HA_ERR_WRONG_COMMAND;- a- {2 D4 k: k* n3 h
}) M% {0 U4 e$ g: M
$ V( S( W. F# y$ Q. r/ `
+ ]& I3 ^) x/ ^3 h
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
$ ~: _# N2 n% f+ ?: Y7 }# M& h# ^
* A; g" L! \ Y, Q( F8 M3. 调用链图( s7 R1 ^9 S d' \7 e7 a
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
7 k/ d1 p" w6 G$ l- P, y2 `![]()
/ l' w2 a4 C( L. D& P4 ?
@ |) J$ m0 w/ h) M1 s# }; X ], W2 G; W6 r% ^: h
三:总结/ W. C c" _4 }4 F; D' s' e7 B
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。( d& ^8 S7 {# D1 p* d# x
————————————————3 z, c4 q- Q0 `9 H7 I
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。' s! c- N+ _" k4 C5 V; K
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415; r3 l8 N3 ^; j6 {
|
zan
|