- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55544 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17615
- 相册
- 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 |+ Y2 d# K6 Y, H7 O8 _1. 讲故事7 s5 J" m# o2 Q- G1 I
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。8 R* M% _9 @2 r+ k- @+ f1 k# @
: y8 P- X: p% E/ _# _8 l* @二:了解架构图
n7 C$ d8 f: v4 b$ I Tmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
; A: \' ~2 b: Y* Z, O6 T* ]% I+ Q8 G3 s5 }& p# P
1. 从架构图入手" _8 I* F* ` x0 M8 e
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。. e1 A4 G* n- M% Q6 y% j
) A' Z( ~$ \' `/ Y: w7 x
0 u* {$ y1 w3 H
0 R0 n; v/ j& a, s7 _, g
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
7 {1 W; I8 N h* H8 A2 N/ g
3 ?# n! p: q& q3 s1 R/ B2. 功能点介绍! D( S# C6 _0 E$ c+ U5 j
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
! K# g: E/ T. o! N! u
6 A% |/ Q% [1 g<1> Client5 J6 G3 T7 w/ d7 \# h
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
0 o2 d$ p$ a! |4 f9 j+ v5 S; n/ ^. T. w4 I" r) z
<2> Connection/Thread Pool+ P. Y5 Y# Y; E' F
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。; U. ]* F, _1 Y' y+ l4 Q" K
! Z$ F6 x. U7 Q# a: H8 Q5 O" [
<3> SqlInterface,Parse,Optimizer,Cache5 \8 O2 h! p% n2 |8 s* j* _" q
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
8 Q/ p% X2 j9 {" e2 W0 j% @0 G# O8 K0 [& A# Z% \) o" _
<4> Storage Engines, h6 h+ C% ?- j8 l
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
1 Z: e. j+ ~2 F& k
2 \2 }; g4 m5 `1 R; z& }+ j三: 源码分析' H. a: T7 p. D# W
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。* r8 J* G- s0 b$ g7 q6 x
; d3 }: S& c& O2 F4 y" }' o1. 了解mysql是如何启动监听的( g9 m, A$ F0 D) u1 X T* U, D
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。4 v7 h/ P) O! J8 |; N
![]()
2 ^8 J) _5 |+ F2 }- l z" b0 K7 Q6 i/ F# K+ R0 J6 \
# y3 @1 {, V9 H3 v8 C3 E4 ~3 C
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。- b$ g) ?/ |+ W3 k& S
9 N2 A6 w# T0 E) ~/ d3 n$ D
<1> mysqld_main 入口函数 => sql/main.cc6 G( `" }* f4 m& i# ]
! |& k; Y" _$ Y# D( f- S2 ~7 {
# C' {; f# A2 \/ }+ z. b' }/ t& O
extern int mysqld_main(int argc, char **argv);
! C2 J0 N7 ^) f: I6 p* N7 P* z8 y
int main(int argc, char **argv)
`% G9 E( F% n+ H/ d{* S, o" ?* t* n
return mysqld_main(argc, argv);
# j x9 s- {* T}
$ T4 U, s0 `. O* F0 r6 c. H* A7 d# E$ h7 R
1 g% n. M: Y6 g( J: y这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。& N. M* ]$ m a4 @% c" B, R
- E) K) c5 z: O) h$ p4 E6 y/ x; t* T
<2> 创建监听
+ i. ]# H: g! P& Z$ `) s" j( h7 H) N, ?* ]
6 z! t9 ?5 S9 J( O$ kint mysqld_main(int argc, char **argv)
* }" n& c- i& g" y6 _3 K1 A, e{% Q! z( m) C0 S" j
//创建服务监听线程
/ l" H3 C+ p( E1 V handle_connections_sockets();
7 i3 \! @4 f6 h}8 d J! K$ Y& m8 `# b
- V+ b H. [1 y8 svoid handle_connections_sockets()
# ?. @4 X( }# i9 k5 [{" g9 N E u. e& v' ?% E( r
//监听连接
|# e# O3 l. n: v# M' P new_sock= mysql_socket_accept(key_socket_client_connection, sock,
. A! ]+ s9 [/ \3 | (struct sockaddr *)(&cAddr), &length);
; x2 k1 h( v) R t+ f7 y
4 N: {3 D; H) v; E if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)): Q; A" T# X8 a# T. q
thd->security_ctx->set_host((char*) my_localhost);
, }' Q3 y" z( Q) t4 @
! y3 g# E/ m2 F0 J; F //创建连接
' ]; P9 l. `3 ^( E create_new_thread(thd);
2 m) c6 Q' ]; _; n7 ?+ \( z! u( ]}3 F! l% a/ |! t8 k2 j; K! x
9 Y- L6 e( M2 C. B- v9 Q, k
//创建新线程处理处理用户连接
) `- ^* A# X I+ astatic void create_new_thread(THD *thd){
& i* X2 Q8 F, U% i, T8 p9 f
3 b# ^9 S1 e: L6 n) N thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
' m6 y) I# E S; }% t" Q
2 }) \5 d( k; p% C) o6 e3 ~8 q //线程进了线程调度器) C' F! }$ a9 w- J
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); 0 e0 c6 L1 r; T2 G" [+ Y3 d
}
9 z" k8 a0 x8 T; k: b/ Z2 _
( F! F0 `( L0 l. a3 t* Z, e: v i# \- Q7 t T
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
9 C* [0 Z3 |6 Q( q0 \0 A) p s
. `7 B, Q% i3 z3 c: v. `0 c! j2 @6 V% m# N; e5 ~3 H, M( k* @
2. 理解mysql是如何处理sql请求; B8 A) s i5 y' t
这里我以Insert操作为例稍微解剖下处理流程:
* ~$ L$ @+ x* V; x' u* h# D& W2 u( u% W! t
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
+ v# o) Z: B8 w
0 `: K- O9 C4 F7 p
0 K p; B' P! @; F. Cstatic scheduler_functions one_thread_per_connection_scheduler_functions=
8 l6 H; @: C' o9 W% k6 G/ C# s{
9 i# S/ r6 T' c0 {' y K7 B6 `& Y 0, // max_threads- Y9 i. n1 x8 p" \8 o' [
NULL, // init4 F1 L) U" n! @7 k V! w& Q
init_new_connection_handler_thread, // init_new_connection_thread
5 N! m! X4 ^ l4 O1 O6 i2 E) F create_thread_to_handle_connection, // add_connection
3 [( b6 P* P+ a ]' k. b NULL, // thd_wait_begin& ^0 f+ A# C) k. i
NULL, // thd_wait_end G9 u X1 _+ p: {' t6 }; q
NULL, // post_kill_notification
0 m2 P, v/ m$ n: R one_thread_per_connection_end, // end_thread
$ r- q2 |. w1 ^: T1 k' f( U- p NULL, // end
% n) P: }* F+ @/ j, V1 X};
( o" b$ {8 v8 u5 h( f( u' ?
! P, `( o5 W, I( |; r$ F# v# n
+ X" Y' C! j1 q/ U# T. ]从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
& q$ n. }; R0 Y" ]1 ~
) N4 d& _: E n<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪1 i8 K3 ]+ u+ Y$ {
2 K6 V1 q `* X/ V# K" ]void create_thread_to_handle_connection(THD *thd)
: [( Z1 V9 c. B{
/ ~! b5 z; t7 A& a5 A/ ^ if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,! R6 T$ |9 ^: e) Y3 F- j5 W% U
handle_one_connection,(void*) thd))){}9 k5 O- q' }) T. e( A0 s L5 Y
}
9 w3 S1 o8 L* S2 h: ?& L//触发回调函数 handle_one_connection
7 @7 k! Q; W' s" s0 R/ y' Fpthread_handler_t handle_one_connection(void *arg)
' i3 J7 z( u! P% y4 D% A{3 o% n i0 [+ |7 x; G- r3 y
do_handle_one_connection(thd);
3 W$ t8 q6 k8 j- i+ l}& |/ Q/ {& c+ k' J' ]9 K
//继续处理
0 S) c, ]" d! D0 Qvoid do_handle_one_connection(THD *thd_arg){1 g$ ]3 b# ^4 \: u, u" g
while (thd_is_connection_alive(thd))
7 I" y! q2 j9 m, C {( l' h( P$ _* u* Y
mysql_audit_release(thd);
/ y/ H# z( b# D/ q. f if (do_command(thd)) break; //这里的 do_command 继续处理7 h# R2 E' @) Z( k* N" _3 d6 U: ~+ b6 S
}
" T. r3 i* d" I}
; W$ k" {. j! P. N$ {//继续分发/ j) @: q( [, B% n i, `. F3 H& R
bool do_command(THD *thd)- T' u' z+ g9 H* R( F9 u) H4 S
{
+ X8 a- K# k$ T return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
0 [$ O& f& l8 T s* _. G8 F}; G1 i# { _0 _5 e. J0 I
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
# N# ` I9 X1 z& K7 [9 a{% I, V; P; | V0 o
switch (command) {
% c; j' G9 H2 G$ @! { case COM_INIT_DB: .... break;/ p) S1 h8 F, L2 o! z( y
...
9 |* q/ R. N' [3 j; C8 E. T case COM_QUERY: //查询语句: insert xxxx
0 b1 W# h, a8 g& x mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
+ i, T) ], y/ u( u6 E' L break;" u. W. n. T% A$ e% h: t
}* W" e& c8 k! q- | Z
}& X7 e$ L" U) M. V) y
//sql解析模块
* k5 R6 W" x& x i( J* ]( K. `void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
. y) z7 l7 k& g- e& W; M{% y7 J5 H- f8 ?- [; r5 Y
error= mysql_execute_command(thd);: y0 G( y1 e' Q
}
4 r" \6 I; `2 K2 v* j5 M
3 O6 O, y" H2 C' B
, T1 l( u0 E( H4 H; [, ^# f" S<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。; ?( P* E: H7 q+ H: T9 _7 j
* M9 |- F5 y/ @, ~" G9 C//继续执行
% f9 x/ G+ |+ [/ Q$ C! W4 a ~int mysql_execute_command(THD *thd)
0 M5 D7 L2 D8 Q8 s1 e5 s8 I6 _{
; o+ |7 A/ n8 p4 h4 X2 i: W! p1 s1 F switch (lex->sql_command)
; A6 d0 T3 v$ i2 E5 F2 T v {
. P2 H* P+ @0 q1 z% U5 E8 m* g: v case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;) L: A% ?6 m ~) L O* \) {7 G
& e( y$ q% y# E/ v' y( d
//这个 insert 就是我要追的
j+ p& w, G% u/ G case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
2 n& }' Y- B7 q7 o; W. h lex->update_list, lex->value_list,0 \6 p* T1 r% r6 w% f% S
lex->duplicates, lex->ignore);1 v, q" a; @; T8 |+ C
}5 S# w' o1 I4 b
}
) ], H+ T% s. J- h* K5 L//insert插入操作处理' N6 C8 b! }1 Y4 |# K7 D
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,3 d" n# Z e5 v; {2 L: n8 Y
List<Item> &update_fields, List<Item> &update_values,
& m6 f% ^5 z) q5 J& f+ ^5 q! o enum_duplicates duplic, bool ignore)) B( L/ z& Y% H& \
{
% w$ P2 ?) E$ K* l/ ^ while ((values= its++))
# g: ?" e- P* V" f: H& Z {
! X6 z- v4 e' W1 K. t. h5 A error= write_record(thd, table, &info, &update);
. A4 b9 h. `" T3 ]$ B }
% \. u1 G+ y" g}4 a4 H& \/ D' Q0 |
//写入记录
1 Z+ h! d1 `% |# K$ Oint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
6 c7 ]: |0 L" w6 q{
1 T" L! Z& S- B6 k5 G, }5 P: V3 F% ] if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)+ H0 T' J( N: {" a1 Z
{
+ e- B$ S% b1 s6 t, m // ha_write_row 重点是这个函数
. c _9 P! A5 p! N0 J9 B while ((error=table->file->ha_write_row(table->record[0])))
Y' [* m5 U6 L9 R5 u; @ {
5 q2 [5 v8 Q" B g* c+ ~ ....
9 e, u4 N. A/ t) M' z }$ {2 `: v- r G; v2 v; g2 i
}# T5 b1 o6 g4 h
}7 A$ N3 f7 y; S
$ ~& u/ @+ g/ h. y6 o: A: l' L
! s8 a ]2 v" u3 c; d# q* `- _# O6 B" b a+ B3 n2 j
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。0 u6 b( w: {5 ^2 w
% F$ x/ f% S7 a! ?
<3> 继续挖 ha_write_row
~, l G3 z1 F, J) Y% E" p$ U; \" T& \- U r- I3 d8 v
int handler::ha_write_row(uchar *buf)- \0 v/ G- S, p3 _) \# e4 p: e% M! |
{
/ t) [1 M: s6 x# D* q3 m MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })( i$ B. } [9 Z" O1 g# I! C
}, y) `. S% m! e5 W A
* r/ U1 E% [) f3 V$ m+ y
//这是一个虚方法
: u( a7 T3 x/ w. g; n$ Q1 ?virtual int write_row(uchar *buf __attribute__((unused)))4 J% p0 c# Y* X2 ?+ b
{/ u! _: ?' L) k8 g. S/ w
return HA_ERR_WRONG_COMMAND;: Z6 F, U0 s" T% b2 g# a9 T C8 F
}
2 c, }1 ~# ~0 b/ z9 p) F' L) B, o- J
/ u9 N9 S' y7 r看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
5 N- Z$ D6 k8 ?% ~6 @ a. x
' D( V' i9 Y; r9 V1 c3. 调用链图
2 u" x7 u/ d4 {8 h! `- a4 G* |这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
0 V+ D! V U n+ N, `7 |; x; Q![]()
+ R) g- _& r7 W5 m4 ~# A7 S) L& C! A7 R
$ e& u6 H" L/ X2 k& Y! d0 z三:总结. n/ U0 o# E( j# T- D! _9 j2 X" M
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。2 ~6 Q% {. C5 z7 g! j6 Z; l! E4 U
————————————————
" v( c& d3 ~# B6 _9 `版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
# H. J( Z* o5 k$ c! ^4 Q$ ~原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
" F. s/ _' E$ \ |
zan
|