在线时间 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 田老师国赛冲刺课
一:背景
2 J) j* @- w6 X. u 1. 讲故事; c9 N! N* ^1 z, l
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。7 g" K+ ^( c3 T7 X6 |# O4 E3 L
# _. x2 s, S& C9 ~3 r8 F. V1 J 二:了解架构图3 o) E- U3 q. h3 k) Q
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
& r5 r# H+ u% c7 t9 Y% E& o( H 4 l$ I7 i7 M- e& ^0 ?/ g# ?7 s
1. 从架构图入手
8 B" W( ~2 _" O5 m 大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。3 E8 k. u- y7 u8 c3 E( s7 v
7 G9 z& O: P2 k6 Y* k8 `
; Z3 Q: u/ I4 y$ h' W+ t( h( m; m
( \+ B) o0 h" T8 Q2 k+ X6 R 其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~% g8 H3 m! `) x+ H/ |* {+ p& @
* O+ k1 [! u3 V& K/ K% T 2. 功能点介绍2 A2 `' M- B: @; f
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
0 a& P- F5 U* B$ b4 l6 s) } + I% b5 S: _& Q" m0 C" s
<1> Client7 \- }3 t5 C7 a- J- _: e$ H6 S: m# e# `
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
" K W R5 f" o. t . ~8 P) g, @+ x
<2> Connection/Thread Pool
" [. V6 B" F! B1 G4 r- t) a MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
" d; t- x* C% K; ]
8 u5 E2 W4 o$ _. ^% y! H6 G) }6 }) j <3> SqlInterface,Parse,Optimizer,Cache
+ u! R. X3 Y( ?" N1 `# p2 Y 对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。- a! f. j; _6 j6 s+ q2 }+ s
) }" _# W# a7 O: Z8 `
<4> Storage Engines/ _( [3 w+ l* v8 c
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。2 i' F- M6 g* N7 b- I/ t1 ^& {
! _1 k @( T' n: t1 w 三: 源码分析
# w5 M4 j! L a3 i3 G, w 关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
: I* x2 z3 F S& |% V% Z ( c O! D1 J1 o- f: v
1. 了解mysql是如何启动监听的
& M) D+ _9 `. v 手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。, b) R1 w L# {2 Y0 I+ s( _
8 A8 e7 S' {- p. p+ Y" O
% M3 o5 h; s7 f 9 g' E' ?7 a9 B4 u% G4 d
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
5 f8 N$ E8 h% I* }, E I % U f0 H9 k! u( w
<1> mysqld_main 入口函数 => sql/main.cc! x5 K/ Z; r: V W0 |- a4 P
% z, q' c# s) y3 i) ]9 M& p- y& l$ p4 s4 r
9 _7 n5 j" z- @* O extern int mysqld_main(int argc, char **argv);
9 m4 \4 W: A, f* s
, O( |- g/ Z* _ int main(int argc, char **argv)
$ d7 H$ R1 h# i1 N2 v/ { {: q4 c$ s8 c% @" H1 b
return mysqld_main(argc, argv);
8 \' e7 G7 U1 f }
0 \% _7 k" I. y
! e. x/ ?% T2 v$ y, N! |7 S% o
9 h9 @6 n; Y+ k0 Y r# u 这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。, w' f' Y% W5 H! b& p/ |+ U
: w* W* d( N ~6 h1 a5 Q
<2> 创建监听' {" z* M& q( v% K3 h. J6 J- ^) u
) t1 z2 U& E. \& J6 M
7 }: P/ \$ B$ ?( q
int mysqld_main(int argc, char **argv); |/ W6 x( D& ~5 l5 O, D- N8 G
{! G9 q" Q7 `) R% q
//创建服务监听线程
" x- h- O5 `* Z8 F! A( E handle_connections_sockets();
# M1 w4 Z/ i( c- P }" N8 O! T( \3 E4 V3 B
$ M8 ]. H4 A. H void handle_connections_sockets()% u ?& c! N% E
{
4 T8 N) P v+ g) q //监听连接
2 Q0 h: J$ o2 x! ^2 c4 G# m. } new_sock= mysql_socket_accept(key_socket_client_connection, sock,
9 y, q6 i; Q+ j; ` (struct sockaddr *)(&cAddr), &length);+ p: _# z3 N8 k: k" h
6 o& b0 R. R* d( S m if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))+ d- p" ~; r9 b/ M
thd->security_ctx->set_host((char*) my_localhost);% l- F0 [+ z& y" S& I& k5 t
$ k3 Q+ o R/ D: C //创建连接
) f( w" v4 f6 C# ~* u5 p, O create_new_thread(thd);1 H& {0 d6 v" g. G" \5 s D
}
3 a; a3 T# m/ l# |9 P3 |7 s$ T& l
; G& [2 S/ o) V //创建新线程处理处理用户连接: L# w3 B4 D8 @ r% o" |7 k
static void create_new_thread(THD *thd){
, L3 \+ I6 d" c. [ 7 Z0 d) }9 S. n, h- k1 [: n9 h) n
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;0 |, R& k7 w- F) G; p1 |8 c3 H- a# f
6 u" m! q; L, K4 A1 u6 K //线程进了线程调度器
# e7 p) W5 _0 b! Y6 L3 T/ w/ g& [5 m1 I MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
1 P+ Z2 D ~( c# O$ n3 I$ k }' C: Q2 d3 K# { _5 T
# e: ~% z' K# v1 [0 b
d& _- N; N' k3 Z' G' s4 p2 B, i* `( L 至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
. v9 i1 r7 U+ H# z " ]* y0 k# ^( C- V/ t0 P/ Q
`! Z5 a2 t2 L 2. 理解mysql是如何处理sql请求& r4 o! r$ j5 }2 d% ?1 H+ g
这里我以Insert操作为例稍微解剖下处理流程:
* R3 b: E0 O w& n& J6 E: @6 p n* `
$ I/ @; Z% J) R! f- @5 y 当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
0 W3 h- `% {7 ]2 b2 g$ q
" Q, O% G6 @) M 8 N5 S* W8 `1 R
static scheduler_functions one_thread_per_connection_scheduler_functions=
( w* J: j& F2 e' s) a9 N! R. E' m {
5 l" |6 ?/ u O2 O/ c* q/ O# w 0, // max_threads s f$ b* R9 i5 S
NULL, // init
7 t8 ^6 H4 \: f- S; S+ j init_new_connection_handler_thread, // init_new_connection_thread( a: H, Y' S$ Y4 {) Q! U1 j
create_thread_to_handle_connection, // add_connection2 s. g# V( ^0 Q( x: E
NULL, // thd_wait_begin! E# S) x d7 i: _/ H7 L4 S8 t
NULL, // thd_wait_end$ b; t0 \6 j4 x$ B
NULL, // post_kill_notification n. {; L" |7 P8 G
one_thread_per_connection_end, // end_thread
. \4 v2 i3 f p' i5 y NULL, // end2 T6 } ?0 k% ?1 \4 @9 C4 d2 s# J
};
. c" }8 O4 @6 t9 C) a0 m % n' w& h% J) x& z( J
" w! y3 Y; F& m; I0 ]4 ^7 }8 {
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
3 |% O9 X [. p$ @/ [0 R
; z( P0 \ Q5 H. ~$ U9 l( |8 R <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
( S \0 }* M# ?7 y. k
& x- p; C& `! U8 B2 v6 W void create_thread_to_handle_connection(THD *thd)
* m7 d7 I+ Z5 k {2 i. F8 X. C) W) d; G
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,! Y' E3 W1 ?+ Z5 G' [1 |9 Y- s
handle_one_connection,(void*) thd))){}
6 e9 a7 d9 P. A& c7 } }
( J! V. W; u+ `5 g' ^# g //触发回调函数 handle_one_connection
8 S7 V* F) B( q! e$ I" P pthread_handler_t handle_one_connection(void *arg)
2 Y; c9 h0 k3 |. I; | {
( t7 O# b1 ?8 O do_handle_one_connection(thd);
/ E# I+ z7 E4 |9 ~7 ? }( @" N' h+ A* z8 b7 i6 F
//继续处理
- [) w! G, g2 M' B+ a$ O. X9 S void do_handle_one_connection(THD *thd_arg){
* W! j) A5 y, d4 Q while (thd_is_connection_alive(thd))% T- B" ^; {" D& ~
{
' d0 R7 W3 L; S& L1 a0 Z mysql_audit_release(thd);
& P3 H f. U7 J }# ? if (do_command(thd)) break; //这里的 do_command 继续处理: U H$ G3 a: H, Q
}5 t1 }2 h8 j M- M
}
3 G; a( d( K! l! h //继续分发8 j+ x$ u7 o: ]) L$ L
bool do_command(THD *thd), A( B' D3 @. c6 a( B6 u
{
9 C" ^6 n5 z5 \7 I' t% V6 Y. x; O return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));% K2 t& t8 W- a4 O
}
; Q8 c( t# `: J E0 n2 p bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)! z, y# t+ ~ l( Y) U
{* r9 ~1 ~+ p& F! t/ v' L
switch (command) {
* a* R8 \+ @8 w( S$ ~" ? case COM_INIT_DB: .... break;
' B& w% s X! d: [8 \0 k7 q. Q ...
/ w1 V3 b5 Q$ b6 l case COM_QUERY: //查询语句: insert xxxx
$ M" B r: B1 M% Z( ~ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
6 {; [- W, b) J# t& [* u% H break;
0 x2 D* ~ m7 O; b- G' d }
# I, C/ H- j1 p. z }" C' [/ i" a: X9 i* x
//sql解析模块
" x- ^" G/ }- A void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
8 F5 X$ m* l/ C& ~ {! v- O" H! d4 h: W8 ~, F3 p
error= mysql_execute_command(thd);
6 ?. P: c) }4 w9 S! z6 J! P, N }
; y) N( {1 n0 G, S% X6 `/ m
; I5 e. k- G; `0 P5 ]6 L
) `5 ?% q1 L3 Y+ m2 x4 S; W; _ <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。* P- T, Y7 q) `3 m" ^! Y/ R
- f% S- x0 h3 v5 J: F/ t0 a
//继续执行5 \3 W9 V2 j5 _8 @ @% v
int mysql_execute_command(THD *thd)* ?& r1 c3 ^* f8 p3 \
{
' G; x* q/ [2 w* V( E; } switch (lex->sql_command) ; y$ q2 Y$ G# \1 r( P/ A
{
: [: A- u# C, n7 [! Q case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;8 m u5 e- l# i; k3 o% J9 `" O. ?% E6 a! M$ A
# h8 a! f, ?& }5 p# b% d# N //这个 insert 就是我要追的
6 t- R/ Y; ^3 |. b; t0 } V9 ^, G case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
6 w! @1 ]' ~0 S lex->update_list, lex->value_list,
* O! a, M( A" a& v1 r) @ G0 M lex->duplicates, lex->ignore);3 b# ]% ?7 r5 t2 B9 g
}5 {$ V$ u4 H3 R5 V: U; \9 T5 Q
}
( w3 C# Z# v, O2 o" W; f: N; V- B; ? //insert插入操作处理
- D/ d E7 |7 D1 g& [ bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
6 v% @1 P/ K0 A* N List<Item> &update_fields, List<Item> &update_values, ~ d! E0 @9 P; v
enum_duplicates duplic, bool ignore)
5 Z8 f! Y7 U& A. ` {8 L5 L& l) j: _- m) ?) {5 r
while ((values= its++))7 g* N& K0 n6 [8 _1 I1 s
{' `( _, |0 j8 U G
error= write_record(thd, table, &info, &update);9 o- F2 s) M# R9 A* I6 f7 W
}
) `# z5 W) {5 v; M o/ v% m6 i }- o+ e! a1 t% `6 e! I$ {: I
//写入记录
1 c# V+ y- I. `% D* _, y2 M int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
( G: t# {) t/ n, _5 `0 g8 X {
# n2 q: N$ F9 s; R. u if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
! w0 U- F( h* O. l y; m {
7 `! L6 b' s! T* z7 j9 R // ha_write_row 重点是这个函数
S( O4 x/ W, m6 v6 M5 M while ((error=table->file->ha_write_row(table->record[0])))4 m6 X% b+ B1 S" U8 |4 R
{
$ K8 }' o/ L, I; f4 V1 M, A! z ....
% Q L/ a3 v6 { }
" ? J+ w8 s7 l }
( f" {- M1 p m9 a8 Y }
) R7 B7 R3 t! o
' s" B" ~/ f! v$ y5 c9 V + I- E; x3 m, b( d2 M+ F
+ y- w! ~0 j H5 a# _; t5 j# } 可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。! ~, b& F: C3 Q+ A$ } v' b
$ `( l, P* `4 F) [3 l4 C) S* l9 R7 z <3> 继续挖 ha_write_row7 o# b$ S2 F8 c" v8 Y
4 z! s& a2 e5 s5 F, u( R int handler::ha_write_row(uchar *buf)
4 l+ v9 Q- c- R5 f5 O {- m& S/ s- L# b! _6 R
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
, a; ?7 w0 H* K$ | }
3 X- k( }2 E+ B* J0 A( d) W
% z, d9 Y7 c. p# K/ s //这是一个虚方法
3 H) h5 n, H% i3 }1 \! a( K virtual int write_row(uchar *buf __attribute__((unused)))
( N6 \- f' l- h) B1 u9 F {
5 g/ N- a7 g. W- j7 c+ O return HA_ERR_WRONG_COMMAND;
8 E7 r% k, H$ K9 Z }/ S. C* Z G& J$ v8 m& S; h2 V, h
6 \- A1 m7 X1 x: J; a% p7 {
3 L/ ~6 o, ~6 I$ W 看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁0 l. N/ P' c( L2 |- D9 p
& P! Z& J) ]; w' v6 p
3. 调用链图
8 [5 ^) \6 l) n; w1 a. U- \ 这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
/ s5 a9 ?! d Y H( q
' |1 p# M; M1 D' }/ r; U; i/ W
; n3 {& n9 }" T" ~5 o: m5 ~
. R$ v& E# B; |6 r5 F2 w+ m 三:总结
6 j0 g4 e N* o0 r, H 大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。8 t1 b6 K p; _1 u
————————————————
@. Q4 A) O( ]1 Q 版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
8 S7 e+ l l' o% y 原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
& R! K* I- Z9 }, h f4 `
zan