- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55541 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17614
- 相册
- 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 田老师国赛冲刺课 |
一:背景, O: L% F4 [& [5 C: X
1. 讲故事! W( T7 D9 b' u) q7 q6 C
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
: R9 S+ ~+ O. d0 x3 t9 X, i5 M [! i( N- s' p" c' K
二:了解架构图* m0 M6 r4 h; W, b# }
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。0 y7 o: \# m9 Y- g/ e' q
" }# u: c2 Y. f! j* j
1. 从架构图入手
% |3 _8 v. A: J8 r大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。0 S* }4 Y# P& y% k6 \. X9 G* A' n
![]()
! e+ s* J% D1 R, h& V
! N* D# v( v `3 O5 k4 H, A
! g& M8 f* D1 s$ h' f3 w其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~5 X) l0 ]4 a: q, g3 _4 t" T
1 W4 G3 A: I; D: Q$ v2. 功能点介绍
1 Z/ v, B) ?4 [8 UMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。% F1 k/ x( Z7 k. l. a" t9 b
; ?2 `6 s8 I/ N2 ^4 o8 F<1> Client
% \$ Z' X" F8 l5 ^8 R4 i ~7 X不同语言的sdk遵守mysql协议就可以与mysqld进行互通。) a) I( J, }0 ^. p/ Y
! C' \1 \0 {( S" I3 J) Z' [
<2> Connection/Thread Pool1 h& w+ O0 b. Q8 M' r# Z: m7 T+ h3 n
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
' r7 ~4 P% F9 i! h5 E1 {
4 j; A4 f/ y+ h<3> SqlInterface,Parse,Optimizer,Cache
% o; O! S" [/ n* p! x6 T0 l7 _对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。# I4 E2 u1 m8 N
3 K6 o8 y5 G3 |% ~
<4> Storage Engines0 u9 ~+ B: [2 p6 Y8 a3 Z
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。; Q: ~+ q; P$ S, ^ |" p% v T( V: D
% R' y6 S7 C. z1 A三: 源码分析4 _/ G( t' U& P0 R
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
4 M! v% p Y$ R L' }- p
" q+ [7 E2 f/ I' t3 v( h1. 了解mysql是如何启动监听的9 m% D8 A1 ~2 `5 R' ^: n1 F: g
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
/ S. O% ~& A$ {. o1 o. z- B ' N# r8 G0 G8 J8 [5 k1 g
) N9 }% R' O5 I$ B3 l; x3 V6 j
2 r; C0 Y7 u8 V% l D
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。" b' }3 f2 t+ K& m
4 W0 e( c- g! K; |$ c9 x9 Q6 u<1> mysqld_main 入口函数 => sql/main.cc$ f9 o, ?5 z9 s
6 ^8 n0 v, H; f1 Y! Q; l' _* {
) S$ q! D- J0 `3 Wextern int mysqld_main(int argc, char **argv);; Y3 Q+ z3 z) r% y7 H- K0 u0 C& N
5 k0 a4 d4 o+ @. Yint main(int argc, char **argv)
2 ~2 e3 ] A4 a& @{& k& r* t! h1 c- i
return mysqld_main(argc, argv);% p6 S) _5 K9 F5 {9 t
}5 K0 S; [& c# X: d& j# m! h2 e. D+ r
0 w3 g [% q( ~. E7 P4 ]/ }3 r
' u9 ~& p {2 a5 R9 o这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。/ i5 [4 L9 [9 _! a- J+ [7 X1 }
' F a0 o, N$ O( k2 Y. t<2> 创建监听
" J v* p6 S) ?. {) S( s. b
) i3 _ U2 i9 d, J5 ?; \
% q# f$ l8 \. \- v2 |int mysqld_main(int argc, char **argv)
0 O4 \/ N* g1 i- M) H{
2 H G( q# b6 E& p \ //创建服务监听线程2 @6 O4 z* m% k b/ R8 @9 G! N% ~
handle_connections_sockets();
' U3 T& h. u- r$ F, q1 _}- f# i3 O( Y2 ~7 Q' c
, K; f8 W* p0 p
void handle_connections_sockets()
9 ]( O! M& D% a6 [: ]- p{
# R" `0 s2 g4 i0 C, L' b" G1 x //监听连接7 R# Y; J) S* b. d
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
6 @% U! T% m0 ? (struct sockaddr *)(&cAddr), &length);
4 [" c: ]4 T2 C! x& V8 h, d4 e0 V3 c# o
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))3 j( x8 K, i. u2 c7 Y# |
thd->security_ctx->set_host((char*) my_localhost);
$ J+ ^$ j+ }+ }# S p: E
* X# P. b# t# N G //创建连接
+ `' u t" T: G6 E& G# L create_new_thread(thd);
7 f, `7 A( N& ^# N+ e" i9 }7 B}
5 J2 h9 y; L1 ?+ {$ {) v5 t% |- q6 v/ G4 P, i( T( }: H
//创建新线程处理处理用户连接
1 D9 \* [2 O* e* D0 c6 Y) ?static void create_new_thread(THD *thd){% N& _5 G9 r5 P5 B0 N
' ]' E5 E( r; S9 M% P- Q) n3 r) h5 H
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;. l, s- z! a( T! U7 C" ^
# J! y% {& D3 W" P* N
//线程进了线程调度器& K4 D; h! @# P+ J" C+ }! W, _8 Y
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); 5 n/ v$ P, Q8 i
}
$ l* @5 l) j4 f' J0 x" v! F$ J2 {
" q( U' v3 C& G* k! k( y2 ]& A5 e* N# O- W" \! e
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
; k3 |6 }. n9 ~& G- n
8 E8 B; q' Q. `- G/ j
3 L! E4 C1 t* s7 }5 L3 m! F: o, n2. 理解mysql是如何处理sql请求
+ a! Y$ t- h! F这里我以Insert操作为例稍微解剖下处理流程:
, Z8 J' ^# y6 g. d! Z
; [7 \7 j% s* g当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。( l( T+ J1 q7 e! I, a6 }
6 N G3 D/ w% E7 B/ D3 i0 o4 M, E9 j6 X i5 q- q1 p8 v
static scheduler_functions one_thread_per_connection_scheduler_functions=
7 ]/ z& N- h% D7 Q2 o$ e5 P8 P{
& U' R/ z K, L- z 0, // max_threads
' y0 l2 [$ o" d8 H5 r4 W. \0 b NULL, // init
( J( N7 Z* u* V) ~8 p; F init_new_connection_handler_thread, // init_new_connection_thread7 t, @ X3 U1 h$ Z
create_thread_to_handle_connection, // add_connection
' [% M, O6 s& o' c* `; h8 S NULL, // thd_wait_begin
: l3 ^. c. t' h NULL, // thd_wait_end
7 T5 \ x% v O1 z5 v/ G NULL, // post_kill_notification; @- ]6 E4 H( E
one_thread_per_connection_end, // end_thread) Q8 b! S& u X% T. f" f
NULL, // end
1 J1 M5 k. y; v};7 o/ w" H4 }7 O7 i+ I
. S* ]2 c. {1 b) Q, c8 E
& _1 p8 a% x# T7 @% R i- X从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
: b5 X6 m* ]( {( H& H$ c
; I0 B+ Z! P; f; f4 H& B+ ]1 `<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
, P, g1 N% e) I& C7 V" d& M7 h
) p* G" a- t1 \void create_thread_to_handle_connection(THD *thd)5 d3 Q# }, d+ L% ^/ m
{
% m" [0 g' L/ N) v4 J if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
: Q' l8 v9 Q* k- P) m; m+ I handle_one_connection,(void*) thd))){}$ F* i7 b) K: d3 y1 M" V, H# `
}0 `' @) X0 L5 w
//触发回调函数 handle_one_connection
5 B+ ]- b. |( B9 f2 s: b# l$ W/ F5 F0 ~pthread_handler_t handle_one_connection(void *arg)5 R7 x ]* W0 N9 I% L
{
- z7 ~- v7 O' P6 Q- T/ L6 o do_handle_one_connection(thd);0 }# J6 O" `; l. G+ p- V( m
}
, z; Y7 G, C1 x# D3 l* D9 `& [* Z! B//继续处理# ` G. i: d* S! f/ _
void do_handle_one_connection(THD *thd_arg){1 S' r" m% O4 |; Q" g, c% ~! P0 w% v, h
while (thd_is_connection_alive(thd))
1 T& ^% U: s0 ]# p {
" N9 v( X+ J) p. S; ] u mysql_audit_release(thd);7 P2 _% G+ j' w
if (do_command(thd)) break; //这里的 do_command 继续处理
/ r) i$ J& {* V }$ V. V# d3 n" M7 T% L, N
}6 [, e4 y3 F) K, r( p! X4 Q
//继续分发! q, ]- c4 G4 v% }# y
bool do_command(THD *thd). S$ g X* v8 G4 A/ Q
{
/ h% F8 C" E2 i0 Z8 n6 [# C return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1)); Q5 g, I* i. S
}
6 i5 k' p& O* D; `+ x9 \& Wbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
1 e/ q4 o1 @/ o+ ]: K: B' Y, u- O: j0 m{
$ ?& i$ c1 ]) b5 C, @8 p2 Y! X switch (command) {
8 Z2 p. A* X( x/ {: v: u case COM_INIT_DB: .... break;2 ~/ ~. O* W$ f& c- Q0 l/ a
...
4 k( N# K2 `5 I9 |# ~) K$ f case COM_QUERY: //查询语句: insert xxxx
- E1 ? D0 M, @$ g1 a mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析# ~* @# e9 t. ]* j
break;# N$ B8 X3 ^/ @5 z& a2 G6 i
}9 {6 S0 K1 F) }, P& I2 u
}1 Q" w, d+ A! j, P; x4 ]: A
//sql解析模块- c1 a: @$ n8 Q; X* |
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)5 k2 P2 F8 r. A
{) L1 P \* M' m1 u5 X# W
error= mysql_execute_command(thd);2 [8 `1 N, y; e; J# ^
}/ T$ e% r5 q- _8 b# F, n
0 @5 M" S9 P/ O7 F2 G4 H6 d( O j
/ z, w* `3 C0 N8 K; n! `; y<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。# H$ [8 [ P- A
% |" P- i" o" S; F% p: ~. F* J
//继续执行
' A2 w+ w2 q7 B. d9 f7 I/ ], Q2 zint mysql_execute_command(THD *thd)
7 u5 a4 R6 [1 Y/ C- ~{
; {8 d, G! t5 g' ^ switch (lex->sql_command) + f: I1 |6 |* i1 Y& w7 b! u1 y
{% v$ U0 B8 P! r( V$ w6 h
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
+ M- s) k" h w. n5 J3 i
7 H: g' E" g F //这个 insert 就是我要追的1 ~; s, W5 Y$ Y
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
& A% n3 F% L' R( K! A2 Q lex->update_list, lex->value_list,# z& o& z) Q4 U* Z, V _) V
lex->duplicates, lex->ignore);
M! Q7 O w* ?/ w3 N }
3 u R9 \! ]/ ~( ]9 X: t* q}
; W5 J1 V/ ]3 n: |+ H4 j% }//insert插入操作处理6 t; H) U0 r4 P+ z5 @" |4 V+ s4 D
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,. p% w# i9 T* c9 U0 x+ w
List<Item> &update_fields, List<Item> &update_values,
& y4 {4 c# Y% V" L) [8 ~3 T, \ enum_duplicates duplic, bool ignore)/ G b. o. X! T, q+ H
{
' C) j2 H- S: w9 Z- E( G* ] while ((values= its++))1 q5 k7 n' B* A4 b4 K
{8 G1 [& g# L; ?% s8 d6 J! i0 m* _: j+ P
error= write_record(thd, table, &info, &update);9 N6 ~- N9 k+ l0 b) _, k( @
}
& j# v0 {/ u+ f5 B1 H* X& s: K5 ~}- d7 K3 p0 y v: H
//写入记录
! @% w& M6 U2 ]% s. E4 Xint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
0 d$ ~2 P! a; u* u# w{
: A6 | P) i; o6 L/ h& Z# U if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
. B/ q* B1 P+ M, s% M8 C. q6 M w {5 I9 N% x6 y! _" U- a; D! ~
// ha_write_row 重点是这个函数
# B0 t5 @& a5 b5 K while ((error=table->file->ha_write_row(table->record[0])))
% V' E; D- x( ^ {
4 b5 b- v+ y/ F: r1 e ....
+ o+ ?+ ^/ h. O8 Q; J4 z }
! y- j- {) d$ h; A& W) f2 | }
9 x. s3 ]9 C+ b5 d, E& M}; \! ?: O7 |7 E! I6 A( w4 t2 j# p
7 ?$ N ~0 N! W' R Q
0 l+ ^$ o8 x! L; I+ z A+ f' Z4 ^- l$ W I# d2 R
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
% e+ H/ O3 x& P2 |- A( j, W6 c0 U1 t( f1 ]+ A6 ], v S/ D3 e
<3> 继续挖 ha_write_row0 D h+ u0 r$ l: g& ^. [1 l: h
# ?( G6 f( q/ wint handler::ha_write_row(uchar *buf)
6 s$ Y, L/ j( ~+ X2 M2 y0 F{) R5 b; g& q( l2 O& \
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
) `- n' ^9 Z: U2 c- c% ]" ?) Q6 C) R}9 _! A/ c, e, [. ~+ W% m* b
& U9 C8 {+ P) ~& a3 c- s
//这是一个虚方法9 c1 P4 M } J
virtual int write_row(uchar *buf __attribute__((unused)))
5 S; j0 {1 b# A4 |{
1 w* R- @( T2 N( u3 ^ X8 v return HA_ERR_WRONG_COMMAND;8 d, D( A/ ~5 G8 S% W5 l
}# I d( y6 @8 u q, _
* `$ M1 V! B8 d$ c, V1 X
) `! A K7 B8 N8 y& O Z% i, ^
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁+ X3 U9 J# H \0 f! c4 W7 R
$ B t( ^! X( ]- t) H3. 调用链图
3 w. s7 d3 ]/ R) X; V. g这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。 w6 A% M% w0 z# O* |8 K
) b2 p2 Y( B1 J% l" d2 ~
5 Y: T: x, c3 ~) E1 V& D) h- E+ P
0 I8 y" O4 X; B% Z! N三:总结" h2 q/ w6 S5 Q$ @
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。5 A6 `' r6 _5 X6 Q+ B
————————————————. P b) w8 @3 L0 f0 {' y
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。4 S% Q5 F( r/ k' Q
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
, {) M: l* m5 J- Q+ `# ? |
zan
|