- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55557 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17619
- 相册
- 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 田老师国赛冲刺课 |
一:背景. U7 R" P1 I5 l+ k& A* m
1. 讲故事- U1 O+ l1 m; Y
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。. r! i6 T; [6 U% B" M) p" `
( l, J+ e5 Y$ ]; z" T7 ]二:了解架构图
( G3 p' f- g5 [# h1 V, Q: Dmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
, E1 a d) ]2 Q, o# S7 ~( P$ ?, ]" }! N# p$ ^; O X
1. 从架构图入手" w5 c( u$ W1 h4 I4 x3 P! P
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。7 S, w' l, {+ Z$ Z0 ]
![]()
! n" b' _: v* x3 t5 f! v
! t6 m' w# F. P. c: g2 `0 q/ S5 |
/ z W: m! m, \1 d其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
[5 I+ c% a5 U5 b. g1 X* h, i8 O0 i* O6 |5 Q) I
2. 功能点介绍
; N" T# L1 h# u; GMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。8 w" H( K: G9 ~) x4 j2 e/ t
4 s6 I5 Y- C; m, ]+ _% s
<1> Client2 E* U/ j+ G1 O7 }$ Q
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
( ~) X( c: o5 ]3 | O% \' I9 Z( G) Z T$ l: A) `% Q3 x
<2> Connection/Thread Pool* b( n$ r; s- K8 L" w
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
) ~( k" W( W) v' f: ], Z' R, ^' [: F8 F; {0 e
<3> SqlInterface,Parse,Optimizer,Cache% P, P# _( `* q0 g" z7 Z
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。: T1 D* z' s; o9 e
/ L' W' [* q" F! q$ ` A<4> Storage Engines2 c) F. R5 c' r. u: I8 i
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
2 z4 Q& _% P# ~( |; j1 G* o- z8 Q9 o. g; H
三: 源码分析" J p6 d# [& ]) V5 M& Z) a. J
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。. F7 N$ J+ M' R, |8 k
N4 a: `0 t! l1. 了解mysql是如何启动监听的' ?% a, s" a* Q6 J8 h5 v1 @% W
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。( ~2 E* a8 [( g5 P% q+ m' ^
* \+ |3 p+ e4 p3 O
7 X4 b: L% E9 G. O
# m& s# d3 @: i* O: p* m2 ?9 r7 `
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
6 W2 I8 H/ N8 O3 V& N% d Y$ O
) w$ z% Q1 t9 X, ?<1> mysqld_main 入口函数 => sql/main.cc
$ B. G$ D1 T% d! | T4 S0 |* T
" \" I, ^( Q7 ?& X3 c9 `0 v9 O7 S3 ?6 K; m6 l. ?
extern int mysqld_main(int argc, char **argv);
% u1 e. U1 d6 a
0 n# A7 @; R% U Tint main(int argc, char **argv)
j; \& Q! B4 A8 G. s{) W& s( M* o9 O. s3 d8 W
return mysqld_main(argc, argv);$ x: H" Y6 F7 W9 s4 C
}
( L8 j. x- D, K: Z \$ q8 ]6 w; x+ F/ C4 m; m
! k K% \! [- A) m+ V- P$ w1 g这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
$ _5 I r) r; \# L2 q$ m/ t* Z* A' z, w- ]
<2> 创建监听% F& Z0 V9 {6 D
6 F0 E7 ^2 b0 Q7 ]
+ l# Z9 S8 d7 A5 s, H! ]1 I/ zint mysqld_main(int argc, char **argv)9 P- j [* _! j1 o/ R8 B
{( U! x9 ^0 l# ]# t* N4 R6 \
//创建服务监听线程) O' _ J% `: g- V! H5 H
handle_connections_sockets();
+ R! z$ ^5 c" p, U9 D1 l}9 v; I9 n: M: c/ T" u- z/ Q: v
# ?& }& r6 j( w5 w6 ?void handle_connections_sockets()
3 Z" P9 J+ L1 V) k{
+ b; y; _' _2 M. v4 V" E6 g //监听连接
/ p4 g# R4 X* P& `+ d( H6 E new_sock= mysql_socket_accept(key_socket_client_connection, sock,1 d4 S8 r7 a# g$ e8 j
(struct sockaddr *)(&cAddr), &length);
, S) I% o: ^! \# R7 ^ R: {! a
3 L7 [0 T' f5 w4 E, h5 o8 H if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
* S4 o! R4 _' f thd->security_ctx->set_host((char*) my_localhost);
' q ~, J0 }" j4 r1 ^! C- K( J! X" K
//创建连接
; m2 A, S. A) C' L5 ?; E create_new_thread(thd);. C4 o; R( Y4 t) p( J/ a6 _# W" P
}
: H/ w% u; |6 t' M3 \
/ O& R: }; d% @, m- K; ~7 ~//创建新线程处理处理用户连接
2 X) c' V/ P' E, a" f( J7 \static void create_new_thread(THD *thd){
/ w# Y7 }9 f7 d& \& J y: h
# U: Q) w) F0 N" ?7 p" O4 J thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;) D# {0 D1 C1 h+ @& j: y# v! V3 B8 L0 P
- X4 E4 A& ]% J# X r( U, [, Z: q
//线程进了线程调度器
5 x7 Z) d) ]/ K" `, I$ G MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); . p' O+ Q3 R- ^; {5 V5 D5 s Q) H; f
}0 n! @ G% }7 F, A: {% Z( Y
( b8 A- y6 J) ?% W! L5 y6 c/ p: Q" f& d( D
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。5 @3 v: V" j' h
; _/ _ n$ S7 F
7 E; u2 L: w6 ]2 }) o, R3 t* X
2. 理解mysql是如何处理sql请求
& r, q, x- J2 O+ B5 r这里我以Insert操作为例稍微解剖下处理流程:/ E: i6 g3 @+ [ ~" Q
( l0 e% x8 a* H# h3 @. n
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
! q1 r4 g s5 q6 ~
, L) c0 W3 [ E( U7 z5 x4 E
* l( [; o# h# p2 F" Mstatic scheduler_functions one_thread_per_connection_scheduler_functions=4 F, q: p. T* x. j0 j
{2 I; `& i9 X* q3 c" y1 S( \4 n
0, // max_threads- a ]8 h1 n5 J) g5 g2 B$ j5 `$ q
NULL, // init( }8 E# |- ]" `7 G' ^
init_new_connection_handler_thread, // init_new_connection_thread
% Q5 p% ?& m% q9 w" H8 K create_thread_to_handle_connection, // add_connection( H, o# t$ u+ K3 W$ M! E
NULL, // thd_wait_begin% m* U* J: { Z. Y: {# N1 x
NULL, // thd_wait_end
3 }5 m4 a! K' w NULL, // post_kill_notification: B7 `# @$ }! m: b# C% q7 G5 R
one_thread_per_connection_end, // end_thread
& S; w6 R8 @+ t3 j E$ r NULL, // end
3 A8 S" ~6 R$ s% R1 C};
8 U. A6 n+ F( X* c! ^) y1 i2 @) F/ J( N5 q3 U7 X; s
. H/ [: f3 c8 V7 o+ Z6 O3 v3 I从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。' L7 N# A( z, S! l2 n
% F# ^! j) L, n. B, G7 p<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
5 E! e) N4 O2 ^3 M7 B/ j+ m/ m8 e- H; E Y1 y; ]
void create_thread_to_handle_connection(THD *thd)% l. F* K u$ }# u, [
{
; ~0 u1 \" O$ \: x( Y' S* C if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,* l m/ Z! X# Y% X
handle_one_connection,(void*) thd))){}
( ?3 C& @. k( d! q2 a6 I}* D3 {3 R$ \! T& a6 J3 D% T
//触发回调函数 handle_one_connection
$ R* g' w5 C) p9 f: v( P6 M) cpthread_handler_t handle_one_connection(void *arg); d+ ?9 ~% L- F8 [1 b. N8 K7 A
{
?+ D9 r: R g: W8 ~& G t do_handle_one_connection(thd);$ E+ [3 p" m8 W5 _! ~1 l1 @
}
/ z7 m* u2 F! _//继续处理
% C. n! w1 c! `void do_handle_one_connection(THD *thd_arg){/ E @0 e3 [6 G4 s# `
while (thd_is_connection_alive(thd))
7 i+ X6 b& A1 P; G {
7 M6 H! M$ K0 Q2 Q6 s mysql_audit_release(thd);. U& G* {& T8 E+ H# S- K
if (do_command(thd)) break; //这里的 do_command 继续处理5 _( Z" F, f- Q9 [5 \2 J2 s2 k
}! [0 _* J& u% U7 |) {0 v! d
}- `" E) {) s/ k; b( Y: }
//继续分发
/ T2 K9 x* s9 [2 L$ L/ G) @* @bool do_command(THD *thd)
) }9 {! V2 P: \$ w) k{0 C' J* G! A0 p8 t% b/ [5 M* E
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
9 t; G& @( w }+ Z" D' Y; ^}; a, c7 |5 D; c1 W
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
0 y7 K9 Y" s0 u# ?{
& i0 B6 b5 b% s1 W5 _; A switch (command) {
* m L5 k3 T& g& J( _+ y& f case COM_INIT_DB: .... break;
1 ?; g& ~% z7 H$ |- s- e! p ...
% N% i1 [) k0 m0 z/ a) Q; O9 B case COM_QUERY: //查询语句: insert xxxx
- |( i1 }8 c$ W3 R, Y mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
2 U5 L9 e& `+ Q. |; Z break;
1 _7 }" f9 K1 `% ?) M; i }
$ D6 B% T! S: b2 d8 d% }* J `}
. E# ]- s) C3 `- {- B# J5 i//sql解析模块
* R% ~0 r5 R4 w) k9 Tvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
* P) M" `6 e$ I9 q! V{
* W' d; u$ y ^ s1 i- s error= mysql_execute_command(thd);5 O, o s1 ^3 R3 F- z
}0 `0 v0 y$ L2 r
$ ^6 L/ H3 g$ Z/ j5 u
( R7 c, s0 I0 N( M) z
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。; j7 E5 u$ `8 ~* m# M& k
* b* W- I& @) D8 G6 ]( T: L. m
//继续执行
( u d" c" g: _int mysql_execute_command(THD *thd)( h) B$ f3 A- j I
{: b) A+ p6 C" f0 ~
switch (lex->sql_command) A7 {+ J0 i, e8 q! s1 R( e& k3 w
{+ X/ ?9 h2 L- d- ]% s; e! k* w
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
) H- P/ W2 F- `" ]! C- n5 Y
. e- K( R3 r+ g9 R //这个 insert 就是我要追的
7 X `- a4 E1 d case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
4 d9 F6 s& w6 T: A! X7 g& q8 Q lex->update_list, lex->value_list,
) n2 A+ I; [: I! x6 ~& R. m lex->duplicates, lex->ignore);
0 Y, p" T4 W( R2 ]# v5 E1 ~9 z+ u( N }
8 I G; f. m7 k2 Q( E* ]9 r}
7 H! X. y- g, U6 n//insert插入操作处理" d, K& l# ?/ G! j6 K$ p
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,) o( w- E4 Y- x N/ b& g" y7 I
List<Item> &update_fields, List<Item> &update_values, 5 A$ R0 i [$ |4 R- U
enum_duplicates duplic, bool ignore)! L( z7 z& B( t# ^
{9 [/ N, t0 w v; M' @$ X7 S
while ((values= its++))
6 ^9 j! U5 r' w- J% l7 X, z {
& Z' l' m5 ], [* W$ v" t error= write_record(thd, table, &info, &update);) g! R* ]# k8 v
}
/ H2 L1 v% s- e; w* @}8 T/ I/ r4 m$ {
//写入记录
" v. c' Z( a& t' U$ M) qint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
! e/ _4 U5 n) Y9 s2 I{
* h9 g- r1 P) R; B: | if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE). Z* r. z2 x& ?4 T( e; K6 f
{
- x* s/ j8 V1 o' ]# _4 [0 R9 U+ e // ha_write_row 重点是这个函数
" n' D# E. k& R# K" ?; ? ~9 f while ((error=table->file->ha_write_row(table->record[0])))3 P- r8 ^$ Y4 I% i6 @
{# C5 G0 T+ m+ q. c x; W1 d
....
3 P( ]4 y+ U9 W9 U+ W4 a }9 E' ^$ e# C0 ^) P! f
}' p: V u: O C! Q9 P
}
+ u0 F2 T- X: }! Q# J. p
& `3 ~! L. G. t9 P7 U- ~9 X- r/ R i; r# H2 n' r- h" k4 O
5 F8 d. v( V+ a2 v8 u
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
+ W+ Q. T$ ` a" T6 z' V7 h0 ~. W1 |9 W3 B& d8 Z E
<3> 继续挖 ha_write_row3 Q& }( r; C+ F1 U! [9 K
4 [% U( D4 }4 w+ o+ _int handler::ha_write_row(uchar *buf)
4 ~: P$ o) K+ m2 @{7 Z9 X; {8 X9 \/ h
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
$ L( L/ S2 e: z2 j3 l/ n}
2 D; S( l) y# \! H, q! d6 K8 a
+ A! J( R+ q1 D5 l//这是一个虚方法
" ?6 u6 H* J1 ^+ }6 }2 ivirtual int write_row(uchar *buf __attribute__((unused)))$ k5 E8 ] X& \" Z4 I) K
{
' d1 V; @7 c/ z6 S/ x return HA_ERR_WRONG_COMMAND;
$ C! S; z+ J: n9 e4 e. e& M}
: J4 R# ~& Q; K! ^) _
T+ H9 _3 D% z b f
- e5 ^- T4 m3 {! O2 a4 x) y看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
1 \# {8 t! K* g' `- ^" G5 N
+ r0 o4 v0 {" N! q3. 调用链图* h7 x3 q# v' j. k0 p% p% R$ W
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。1 m: v2 Y6 K# a) l9 r! Q" j
5 l0 O8 ?' w! o* z
# j2 x' W9 }& N9 ]
R* H8 \, {" A+ f三:总结7 c y2 f3 o7 N
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
6 c, D4 L7 h/ S) y. w( l8 I————————————————8 j3 Z9 \5 U5 j. N, x4 a; O- n
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
/ N; w3 p: u" N9 h7 ^原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
}( I2 w4 f' I1 _ |
zan
|