- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55511 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17605
- 相册
- 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 田老师国赛冲刺课 |
一:背景6 m+ r% p( L/ N4 D* t
1. 讲故事
3 K1 |8 W' ~/ A8 g- o3 W最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。8 {; s& H5 d2 F2 m3 ]
/ c5 u! C7 i, k6 c( t( o7 w二:了解架构图
8 F0 Q( ^' Y- F/ q8 Z; X5 Kmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。& t Q" c" h: h$ J$ r
$ Y0 I# ]( X9 ]1 G, @$ h# I5 U5 L
1. 从架构图入手/ `/ ^6 a! N% x6 L( O
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
3 a; K- z+ L4 u0 I . V& n- m% t1 q I: S3 L
V" A& s [4 E, c! ?
5 |- N3 z2 X+ x9 U
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~" q( {! X( n7 v, W3 H( N2 I
- h) y }& U1 [6 T
2. 功能点介绍# Y7 z j0 ]+ a
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
; B$ P- S/ {* q: y- I) Q
0 ~# L" M) U2 V; L# P0 L<1> Client( c6 C1 x; Z9 |0 u. `* L: T
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。0 F5 s, r g- v4 Y. ~
8 j5 g6 C% Q, }2 n
<2> Connection/Thread Pool' E# Q) ^4 J x( L: x
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。1 I+ e% Z1 b9 w! F/ P8 l
% l$ t( e2 K* h( R- D
<3> SqlInterface,Parse,Optimizer,Cache
H! d; X4 {5 C7 J: B对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。# _( \: o8 a9 w* f' x
( e9 a4 R* s1 p2 I' h% R<4> Storage Engines
) v5 ]7 K5 R4 M# Z负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
. d8 L# Q0 Y6 |* n+ t4 w$ X- o. S5 ?4 g& q! k
三: 源码分析
L6 ~; z7 C9 H4 y+ J! ^关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。, f" j" p* A5 M8 M; d: U; j4 u! E$ H
" G& W) N) h* c: P9 K
1. 了解mysql是如何启动监听的
1 f& i" ~2 m* R' @: {# T& ?- d0 N$ f手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。1 X4 ]" r0 }! `3 v* i
3 e3 m8 n1 p4 M' p6 u
+ ?+ o. i/ b; d6 l
% E, G0 G }% ]2 a从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。8 b# f6 p: `, ^/ {5 z
9 j" a5 k- K% Z& _ X% o
<1> mysqld_main 入口函数 => sql/main.cc/ j$ i9 P; f: T. m. \; r
3 q7 w) a* U. Q% K6 O& I9 U& `: A' l6 X+ u8 ^9 b4 C% e. m
extern int mysqld_main(int argc, char **argv);+ H3 x, N. k7 y; C% ?! ?
1 I, k* Q }, u& ?% u" c( r* Rint main(int argc, char **argv)5 Q( R* ?6 t( E' |# h
{
! g. R8 D9 E1 }- T: l# q return mysqld_main(argc, argv);" O! L$ B! ?" M5 u5 k
}, P" ?* I# s% A
8 K# A }( c! @ c+ V2 u6 y, I6 i5 ~% _6 e7 q
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。0 P+ b3 \2 \" P1 W/ ]
! Q9 I% _4 Q8 P5 S<2> 创建监听
$ e. l# C6 R3 O6 I2 J2 E
S, O; _# `/ f4 H) V" A
' X! X0 `6 T* ]# j) q" C8 C, D* Gint mysqld_main(int argc, char **argv); n/ t) s( h p: }
{
0 z5 s4 `( x* M6 N, m& K //创建服务监听线程* f! ?) y; {0 M, f0 d% P
handle_connections_sockets();4 O- p+ m. h" t1 K6 }6 K
}' K5 l, j9 S; a! k
& z& P( @8 q8 jvoid handle_connections_sockets(), q a3 H1 O; o* l0 t( _ J
{1 e) n/ r* s: ^6 X% d+ P
//监听连接
+ Q* F- [; X3 P4 |! f" q/ { new_sock= mysql_socket_accept(key_socket_client_connection, sock,
7 |( l( L6 j5 G! P (struct sockaddr *)(&cAddr), &length);5 m1 U1 o# u; P7 v
! \# r# |+ E! M7 {3 l" G3 ?* n if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))# E2 r/ \$ H6 f! q
thd->security_ctx->set_host((char*) my_localhost);
5 H- s3 k6 G2 B$ C
+ f4 N m+ k$ n) ~( M //创建连接4 n& F, F* w/ S. C
create_new_thread(thd);' R; o# }. g5 V5 a
}- r; X2 @" S0 L4 A9 k' P
3 A" h0 P' b2 u1 D( f o//创建新线程处理处理用户连接
, H' p, M. F. R9 f9 l5 S6 ` ystatic void create_new_thread(THD *thd){9 R: o! F ]& `# j/ ^! [5 M
9 |& Q$ e& z3 t# f; Y' { L0 S7 }. e
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;3 y" S) c& s' ^/ B! N1 V9 p! U- a
* ]% S9 E; @: n. v
//线程进了线程调度器
% F9 Z" [3 J; |7 b- L4 u) } MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
$ U! |* l6 c0 b8 |+ j}
2 l, H+ A# x+ G( {& V% k# I, q/ E/ j+ |
% I4 _: Q' X% W' ~( v
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。0 [. D& z/ D% L4 y; [1 k
5 u e6 c; o% ]" {- ?0 `7 A3 p f* `. l9 t
2. 理解mysql是如何处理sql请求
) }' e4 i$ `- N5 W这里我以Insert操作为例稍微解剖下处理流程:- K6 f' d/ w( z# w6 F
9 p" \8 r6 z+ _) D6 _当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
8 l) B! b) V3 n9 n ^# y; H
* y( w( ^ W4 `; c& q+ [0 M: D- C0 w0 S2 @; Y
static scheduler_functions one_thread_per_connection_scheduler_functions=! ^2 Z9 [/ T' Z' Q( w. P9 i
{
: x! [0 T5 o7 S7 N, y 0, // max_threads9 v1 @' `3 Q/ E8 m8 f
NULL, // init
; f6 B: C p+ S9 D7 ]" f+ x init_new_connection_handler_thread, // init_new_connection_thread
; ~0 ~7 Y1 v' C7 t+ ] create_thread_to_handle_connection, // add_connection
( W3 B* k2 u4 r. }: G( j" F$ ]8 _3 A NULL, // thd_wait_begin
+ n1 m' T! |6 d3 @ NULL, // thd_wait_end
8 I( l6 A! z& U NULL, // post_kill_notification- p' [, q/ I- j2 T8 w+ ]$ R K
one_thread_per_connection_end, // end_thread
0 K; ~" Z* J' `' B NULL, // end u9 q' I# s+ b. j' j2 ?
};
0 G1 R3 T$ L" i! _5 h) Y/ x- A
- S- g2 g9 S. P3 S+ ?4 B% ?
7 S0 k& g' P* C( x" B从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。$ W4 C% Z" V: b" L
) P- I2 k- v0 h7 ~, N% X
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪6 e. I q1 c7 o5 x# y, F
) R1 ]# R% j( J1 @2 w/ W# K" V" Vvoid create_thread_to_handle_connection(THD *thd)
& F5 _: j% G4 e$ ~{! X' f0 S# ~6 ]7 r j) Y* ^
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
4 n. m* @" M1 S# G ^, o handle_one_connection,(void*) thd))){}3 e' R7 n v* g: P
}
, x8 r! B, O0 _2 W//触发回调函数 handle_one_connection
8 L* ] s I& s' w" G2 m: Y! }pthread_handler_t handle_one_connection(void *arg)
6 Z3 R( `* B0 `! w3 L1 y{+ P+ ]' F5 O p' }7 d
do_handle_one_connection(thd);+ A1 p. |4 B2 X
}
A# N5 i: d: H1 l; W6 ]. q. N, }8 Q//继续处理/ m: i, ~$ ?* S" Y! |
void do_handle_one_connection(THD *thd_arg){
' J. B/ D# }4 q0 B& ^" r: N while (thd_is_connection_alive(thd))
6 ?% d! A% ~% D" y7 ~ {
, }3 j! } e9 L mysql_audit_release(thd);8 n+ ~7 p7 i3 h4 B6 i d
if (do_command(thd)) break; //这里的 do_command 继续处理% W5 F) n% ~* L, l1 t
}* E! b8 f! \) q- H# v
}8 |9 |+ Z- X0 M8 f4 F6 R
//继续分发. Y: f+ u$ k. S9 Q4 ]$ @7 H
bool do_command(THD *thd)3 A) ^' \9 e" J4 a; ?
{( w( n9 i. l9 T; d2 f. J2 a
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
. N0 _: R- n9 n" C7 N K( S0 r7 O}5 |9 g) H. P' Q0 |! W- t) X
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)8 c0 n7 ?, {" ~* _* r
{
5 A5 c. z/ Z0 G, s. n switch (command) {* E+ _) E' r: |' o: q
case COM_INIT_DB: .... break;. C1 I1 l0 o- f$ n0 s# p! V% k
...
( W$ F' r/ F; Q- x" b9 j case COM_QUERY: //查询语句: insert xxxx
3 y$ @" {( h; G# t! ] mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
E( {; T- z' Z3 @( W break;
. e: T9 [1 U" e& f1 B* ?/ ~( S! U) | }7 N# y8 H* Y* ?, Y" G5 J$ r# Q1 y
}) x6 p& P% _0 [' p$ P8 W
//sql解析模块
! z* h: ^1 G- ^1 ]% N5 hvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
, F0 p- l4 H% K) I' r2 g2 ], V{
1 t3 y6 c" H6 c9 z error= mysql_execute_command(thd);( j5 P+ |, p! M+ V. H
}
6 w& J$ c% f6 V+ U' f# n' Y1 ~+ ~
, w( R3 z4 E, y3 }/ t
9 V& `( J% [. _" s9 U) X* P1 h' M<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。 ]$ [) E* b7 _
0 I" w& Y% {/ M* q" N//继续执行" h* i. K% a; T3 X! w0 D& E
int mysql_execute_command(THD *thd) P# y# B% t7 \* r/ q7 u/ _+ B2 \
{+ U; H( T- m" k! v; m/ b- F9 `2 ~
switch (lex->sql_command) $ L( e8 [, A! Q( h" r
{1 r$ ], E* U% k( Y" A7 i
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
" @: i) z1 ^/ r, }6 {3 {; C0 ^5 c0 Z2 `3 L
//这个 insert 就是我要追的* h }3 D5 f% k
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,* p$ x) i, e @+ x" j
lex->update_list, lex->value_list,
5 r3 j4 j3 q" I+ H2 p/ P! q9 a( n lex->duplicates, lex->ignore);
+ R- B9 p( b6 X! ~ } n( [3 Q+ M; a7 `% I
}2 d4 C) J# l& V" W0 i
//insert插入操作处理
9 }1 y; l7 x% W- ~0 Bbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
4 }) }4 t; |/ k c9 O5 ` List<Item> &update_fields, List<Item> &update_values,
8 [: o+ y3 h: j enum_duplicates duplic, bool ignore)( k6 ~! Q" h# v$ ~6 Z2 i" @, R
{
1 U0 \; }6 h% ] T6 m5 r; z while ((values= its++))
, W: h( {8 _5 I2 U' j {
2 v$ i) x3 u. A( I1 } error= write_record(thd, table, &info, &update);. j) V# ]) ?' l$ @ c! v2 }
}# T; V7 @& [; l- W6 A% y7 x p1 k
}; B" e( i: R7 x; M+ m& n
//写入记录; |4 s! w; v5 V$ j
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)2 b d M+ F4 [0 ^: t
{( l: i$ D. L0 a
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
3 [+ P" E1 x2 j& B: G* V5 I {
. _. g8 i, W7 E4 K/ }8 d* h // ha_write_row 重点是这个函数
) a" _9 |! h) x/ d) a; a" B while ((error=table->file->ha_write_row(table->record[0])))% l# h1 f1 p$ _ b0 S
{# p, g0 N6 Z- r4 K
....
T$ `8 u5 }6 E- _4 i }! X- T) O$ H; u) [4 R( ]: D, s
}
& v" X F4 t+ h2 i1 f6 X- @}) L2 B9 ^4 o# A5 W( {4 O8 e/ ~7 b& e
* o0 {+ X8 m$ I- \7 q7 S3 A
2 t2 \& Q; @8 G$ ?# n" g' Q" Y+ Z8 X5 B, x" E
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
* u% F% p8 [0 Y- b4 {8 Y) R
8 O+ M% |; V+ v$ \' C<3> 继续挖 ha_write_row% C. J7 b5 x4 Z
) r h F- S/ k w" }' X- t
int handler::ha_write_row(uchar *buf)
8 l% e9 C- S8 N, Y- ? }- V) b{, s" H5 X4 U2 p$ ^( u: x$ I
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
7 V; ^1 o( }% A t+ @& ?7 l7 [}
" K/ A7 h$ `) v7 F
% P5 v+ D5 J: l! w. T//这是一个虚方法
2 C1 W* A# V5 P2 ]: V* e; O7 Evirtual int write_row(uchar *buf __attribute__((unused)))* {. A' B) I6 ]+ M; m6 x" m
{/ t$ _! V* C) R1 W
return HA_ERR_WRONG_COMMAND;4 G; k) L6 t7 P' {
}
! W! E- D# L+ {
. G" ?$ l* W& ^- F% H0 F7 |+ F$ \! C4 G3 _
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁7 ]4 l% V. K% Q4 U; H$ n
/ y6 W+ D! P! e( g( }) U' _9 O X3. 调用链图
/ }& c. S I- c5 H: R这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
+ U. r7 v. y5 a & G2 e7 L$ E& ?) }
1 @5 `1 G9 y7 N& y8 u4 F* F
' Q- r3 a6 G4 d/ f w @3 m三:总结4 r* K+ s: k6 a, L: F
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。% P" H( C+ {8 s# T5 q i7 ?4 q
————————————————
6 |- A9 [4 u' Z) n! M/ L2 h! M版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。$ f4 D3 T- U" _. {
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415: u% F W& o+ X' J* k
|
zan
|