- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55507 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17604
- 相册
- 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 田老师国赛冲刺课 |
一:背景9 S! m' r. z6 X4 l7 B& ]( Z c
1. 讲故事
. P1 s1 H ]% Q' ^- }( n5 `最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
" E! D$ T( }( G2 j( ]
6 u$ u7 w' {. l二:了解架构图
! u* j5 U. ~( p( h, R3 {) V- y+ t4 Umysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
6 S( F2 p2 L) o0 |8 k' b
% e, s3 w% i. C3 Z& X* R& x4 Q1. 从架构图入手
: N$ j6 O5 E% n" j, i3 K大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
% R7 L* p4 E) k7 |( O+ [% }$ [![]()
- b7 ]2 E- I' Q1 h U; ?0 D7 t
1 |1 ^6 f$ R/ w+ N# a其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
' F( c+ c- z# D) b: t
! O w! X" K% H. G6 |+ g! ~2. 功能点介绍
Y" Z7 v6 b7 X6 ?% i' QMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。2 c) X0 n2 `9 u, v/ O
9 X. _1 H. @* A2 o- @<1> Client1 U3 b2 x. W, {1 g* O: n, L1 i9 R- Y
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
* G: y) }0 I: h' O& w
) J' v& C/ m5 `+ B$ e5 U<2> Connection/Thread Pool
$ q2 L7 H- p/ ~6 k* rMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。0 p& Z8 @7 _! @' z/ W
3 Z+ F$ m% {1 W M<3> SqlInterface,Parse,Optimizer,Cache
" W0 x6 D. G; w& U `7 |对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。# t6 u" ^$ x4 m
8 w1 f8 K" f3 U" e<4> Storage Engines
+ H; l9 @, r I: T负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
+ \$ G5 C) F0 S7 h3 V# Q2 |8 ~$ }7 {7 I9 {* T8 [+ b1 X5 E5 q* t
三: 源码分析
' g" D' o5 V; L, ?( _) |4 V关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
. t! y7 a- Q' l' Z) G3 i. ^8 @* b, u0 o, n9 }2 j6 |* a
1. 了解mysql是如何启动监听的 D! X" ]$ t6 b5 w2 C* r& Y/ N9 T1 j
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。2 n. X/ ~/ n' A' \" M" Q
8 {* d3 }. [6 o/ G: i
6 Y, i6 J4 O m) u( v9 y6 [1 }+ |( l
' _2 }- U! `+ V" i: ]* d$ L8 @, C
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
8 \, F, d/ N9 R$ t! y1 q, G3 r2 b$ ?4 z. c8 O" g
<1> mysqld_main 入口函数 => sql/main.cc
$ t# I0 j6 q' v; ~4 l. r; T* @ y: D
9 r+ y+ o) C- k7 V$ ^8 Jextern int mysqld_main(int argc, char **argv);
/ i+ |" a% c4 j" j0 R7 n, Z0 t2 o2 H5 o, P
int main(int argc, char **argv)
9 v' i* p1 E7 L; E- t- X{) z. e2 x: W& Q+ @ \6 M; t
return mysqld_main(argc, argv);
D9 U/ H& z9 j/ [}; m6 P, b( a" B" k
, }+ F) ^1 p* N) l9 Q* l! j
2 Q& L/ L5 ^- Y2 E. ?这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
5 _& l( J0 O/ M$ i
) U; x4 x8 |7 Z/ J1 _<2> 创建监听% E+ i$ t) ?. E" P {8 v# }
# _" S% m- R- x
! @9 I: F2 F# o6 |' Sint mysqld_main(int argc, char **argv)
' R/ Q( Y1 d* K# k' c2 V{
8 Y. r1 C- y, [" U: w* ? W //创建服务监听线程" w; e6 d& a( ?
handle_connections_sockets();+ H7 y0 [8 c/ \
}1 a+ w3 L7 n7 M7 X
7 T' M) D% ~3 n
void handle_connections_sockets()
. z2 L4 ^: l; R, X3 A: H9 t/ T{
5 r) i' q; t: P! V. i //监听连接
/ I- }, r5 o5 M5 Y" S new_sock= mysql_socket_accept(key_socket_client_connection, sock,' G6 w; h2 r2 C9 S! Y' F
(struct sockaddr *)(&cAddr), &length);
# y. D8 H, z4 Z. C& f8 Q8 }1 t; u
% z0 B" f# R2 }% N* }6 [% D9 m if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)); A( Q7 x. u) j" h! Z3 t! G
thd->security_ctx->set_host((char*) my_localhost);
; G- Y9 n& L+ A- v8 v X
& S1 N/ Q$ ~ @, |* q6 e //创建连接- f6 }: _" G0 j! m1 L
create_new_thread(thd);
+ R, X* Y, B' v2 i}6 }1 ~1 O4 R/ m
; R/ Y$ _1 s2 K1 c, H//创建新线程处理处理用户连接* G$ A) ^1 _% f" \
static void create_new_thread(THD *thd){- o; R9 C; l% ?( o% w; o3 F4 S
/ b* j2 S& I% z, R& t c" g
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ B/ F; z* q% r! { c" H" Z+ ^; V. p* i7 _. L" D- x& C `
//线程进了线程调度器% F: E$ P7 y* o ?9 W
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); $ h3 A! |( j- W. U8 L( H9 @
}) X! @! I( U1 Q% t. o) J
9 y: ]/ y0 H5 N' \% o% S
& `7 I4 s1 ?4 {6 k0 ^) C! q$ f0 D至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。. U+ N' F0 L/ p' c$ S) z5 Q. M
- d* W) \- g6 `) s/ `
% M* Y& F( y7 C1 Q z
2. 理解mysql是如何处理sql请求
+ d* Y. w5 l/ ]+ X. q2 s5 }这里我以Insert操作为例稍微解剖下处理流程:
: U' L% p7 Q& A2 b& d% c
. l/ D/ z: M4 u( O+ ~4 y当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
+ o' Y2 T" G& Y* ~4 w3 U/ D+ o' \" D5 a* ?4 V3 v
% U' E% `$ d( Z
static scheduler_functions one_thread_per_connection_scheduler_functions=
6 B! W3 L6 e# @{2 R9 L8 L3 U0 O8 X6 c2 ~
0, // max_threads
4 S, H. }( b3 l b NULL, // init
7 e$ r- E; G: o2 H init_new_connection_handler_thread, // init_new_connection_thread
% t. X) [7 g4 e* J create_thread_to_handle_connection, // add_connection2 f X' b# I- ~, I
NULL, // thd_wait_begin
e& g0 H! C2 h3 e NULL, // thd_wait_end
- }6 v/ e3 M0 O3 O6 P# N NULL, // post_kill_notification: W/ t% t. t4 O9 k5 Q
one_thread_per_connection_end, // end_thread5 b9 v: ?- Y# q& V9 d. |+ b
NULL, // end
* b9 m1 w' }) N& n};
, ~. {0 U0 k/ e9 K3 R3 @1 n; D# z. p! C
4 x7 E, y o( N2 u& m2 p从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
& y$ k3 M+ E4 L" |
$ c. x A$ a8 w5 P3 ?* p- Z, P7 j<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
]7 I% \; o9 z( k1 x& ~- ?$ C- f. r" b( |8 v- g6 d
void create_thread_to_handle_connection(THD *thd)9 H+ U7 }( `* G7 l3 g5 g l& r
{
# O3 }9 M9 k5 v; ~ if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,; M3 n3 c$ A! @! R6 ^) D5 V8 h1 u
handle_one_connection,(void*) thd))){}5 _& u- g8 G' _0 C/ f; W K
}
5 h J7 g( e& C4 ]7 D& l//触发回调函数 handle_one_connection
5 t3 P7 j( b3 D* D/ g9 L) K3 cpthread_handler_t handle_one_connection(void *arg)
4 G" d) k0 ]6 `% }{6 D( Q& [% }: S3 e: ^5 D
do_handle_one_connection(thd);
9 j9 t% H! R7 I}9 Y& y! E# t G2 k! x
//继续处理
' Q* Y$ k4 q) ~2 T* cvoid do_handle_one_connection(THD *thd_arg){
% |1 L4 X z: W; O while (thd_is_connection_alive(thd))( J% c' T( T: r" X$ A; B
{
8 @" G4 t7 ^$ g3 W( b/ Z. b mysql_audit_release(thd);
1 Y( ^+ I$ m! E# l if (do_command(thd)) break; //这里的 do_command 继续处理
/ I, c% {! l; M1 H T% D- Y }
0 r: Z! r: [5 k2 t5 {}
( l0 S9 M1 E" Y. X+ x- r, e//继续分发
* l) r4 y# D0 C# C4 Ubool do_command(THD *thd)
5 v4 H+ i3 ]( u$ L{
$ N/ r' t, r( O* S4 H6 } G4 o; | return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
|' q E+ `* H* g; V}
( k+ ~* \3 @! P! ^: K: lbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
b; y0 L$ v3 H- [, V. `{
) Y8 ?4 s% u$ C/ t switch (command) {0 \& o# l! e2 Q A& H
case COM_INIT_DB: .... break;
6 |7 D/ h- O% { o2 F" x. e ...
8 {' ^# B! Q- h6 f/ c0 A/ m case COM_QUERY: //查询语句: insert xxxx$ Q" Z! Z( a. g5 ]* L
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
1 q6 D, o: x. C' \5 {% S break;
6 c* t I8 _3 w) r/ Q }
% {+ s, x" n9 |4 t# S7 ~* D}& a* w- o( A4 s9 E/ H# c$ @
//sql解析模块4 s) y6 `8 m) K6 J6 ~: z% x
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)' g* j7 D1 E* _! M
{
, w) m: U' P. J) o: v/ p; |1 ]1 L" V! B error= mysql_execute_command(thd);7 u6 M) w. p6 {! }( E7 @
}, ?4 v E$ Q5 m8 m( H# b0 _0 v( _7 B6 m
" Q& r, X s/ ~3 m2 m9 V. o& U( f* E) ^5 P
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
' ~ j+ x6 D5 |# K' l
) f6 S1 }1 ~- \3 o% y//继续执行
0 P9 T, ]$ o7 s: j; s! j1 s% iint mysql_execute_command(THD *thd)
6 t. j( A: B. M' B/ a& U2 r{
: x; s! O q4 I* f/ x" F ? switch (lex->sql_command) , I/ I2 D$ ~/ C/ y$ c
{
3 K3 G4 O' X6 G7 k( F3 h case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
' }6 |: \( O" [ V3 i' e
) v6 M2 c5 p1 Y Z2 g //这个 insert 就是我要追的$ \9 \3 S2 C4 [5 l) ]* b* Y, y
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,9 q! Q- k9 \2 Y
lex->update_list, lex->value_list,- W7 A/ J2 G# S$ u5 V& f* V$ n
lex->duplicates, lex->ignore);' U7 q$ B' _) A7 x
}
' S6 c; T+ _& {* B$ d7 w9 w$ M}- j4 t! r3 L" I. V I0 X- W
//insert插入操作处理& R7 j4 w) K9 x& q1 H0 d
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,. S/ U* w1 F( S% s
List<Item> &update_fields, List<Item> &update_values, - W" B2 v" y. {$ `
enum_duplicates duplic, bool ignore)
: A* H. v( K, T) O{
1 j3 s- C5 q7 f8 M while ((values= its++))
, @+ ~1 T9 c. N {; b! S. \9 a" K' R% c& k- {5 r
error= write_record(thd, table, &info, &update);, B- D2 h) _$ ]# z
}% K0 L& J. M) E4 D+ h4 v# v& A: o) {
}
3 N4 G% N& {4 _: I//写入记录
( e4 ?5 v4 m* A- @2 eint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)& s5 ?- S) ?+ e3 |4 Y2 X5 k, X
{+ D3 }2 Z) T. \
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
9 g5 h& C. F3 x {
; A% X9 |8 c R% ~/ K8 Z // ha_write_row 重点是这个函数1 ^7 K, j$ v+ B3 F U6 u, L
while ((error=table->file->ha_write_row(table->record[0])))1 N4 T0 r5 ^* F4 j( V1 T9 D
{5 v) A2 L0 ~1 X: ^
...." L: F. y" n' _# A9 \
}
, |# u/ N2 P$ N" _, g4 j5 f3 u }
/ c' ^6 B v5 g; G& g1 U6 q}
3 B" ?; d. X8 z7 x: n. I% m+ Z6 s8 S j1 q( }, g3 w
4 Z6 ]) y) A+ Y% i+ \* H3 H2 g) b! S; E5 h0 \7 s) k( B! }0 I2 v
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
+ h, @2 I. T! Z! x6 L' F3 p3 Z; S* d4 i. u
<3> 继续挖 ha_write_row
4 x8 q4 R0 b- F3 _, @
8 A. y1 b6 u) j" I9 O) p9 @# N% x, {5 zint handler::ha_write_row(uchar *buf)
, }; f! C' W; P; R, A5 ^{5 J; Q4 R- w3 _# s
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })# D7 S( O [) [4 N; T0 j) u+ f
}
1 H+ a. s* E2 ]$ `# Y& b
; |2 f# i! x8 A3 O/ y- S//这是一个虚方法4 _4 r: o- c" e- e; ~
virtual int write_row(uchar *buf __attribute__((unused))). y9 G: s+ ]" c' h- @+ d E* Y
{
$ g! m4 z% ~1 X0 N# z return HA_ERR_WRONG_COMMAND;: \. i. G/ l9 N# f5 W; ]! E
}' \: h7 R& L# H3 u: a
/ g5 U5 n) X4 }% j- _" p% n4 g
3 q, e. y5 W$ f
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁$ m( }: ^1 P' c* Z# o
5 ]. x; L' P# H; ^( }% O! _2 e3. 调用链图
& K/ r! F1 w" C5 |. e3 Y# o这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
" _5 ^7 o. ?/ s![]()
6 s$ Z8 A# a3 m6 j# b K9 {* r s3 P9 [4 q! P R5 C: h: `
4 N# G5 i: M/ l% t3 M7 y7 c
三:总结
, z- t& j4 g; C" |9 E) S大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。 d. y% W( h; `2 r1 u/ p
————————————————
% {0 E8 P3 d8 `0 b; [' h版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。5 D* W) b5 j: V
原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874156 J3 j0 p5 t2 P2 w
|
zan
|