- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55522 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17608
- 相册
- 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 田老师国赛冲刺课 |
一:背景9 ?0 p" i1 a0 q! Q9 @0 g3 W8 y3 E# L
1. 讲故事
" R( j. Y% E2 T最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。6 s4 O+ f3 Q1 r! q7 t
' G. i B; ^# c& F' V
二:了解架构图
0 q. n; F' T- G/ f* Y5 y+ zmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
2 e4 E, d% l2 w6 n/ v7 o, Z' s& C7 f9 S
1. 从架构图入手
3 B3 j# B5 u" Q3 n7 ~+ t2 x. U大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。+ D# k! ]* F6 U$ G0 Q' U: S$ N
![]()
J4 @# F. |6 W* _8 U& S* u- Z7 q
. |) t3 E! O) E3 I: a: J. ]其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
* _: h0 J7 `6 p7 M/ l0 b0 V( k/ X+ H% ?( \
2. 功能点介绍
3 B* n* _/ c: w/ MMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
" z. s1 X4 j" Y2 E+ F$ j- P4 M6 a% |1 }5 t; C
<1> Client t; j5 p7 U; }2 k/ v9 q
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。& Z. {8 V% Y8 i$ k
& P& c* c& W( l8 D; I4 B( _
<2> Connection/Thread Pool8 b6 C% ]8 z' P: o# i9 P; {
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
* h h) q' v0 R( j7 P
" b' j6 k& B; `) i% O<3> SqlInterface,Parse,Optimizer,Cache# W+ t9 W7 U, w2 ?9 V- S
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。* t( ?7 K8 { F1 r, Z
2 w7 R1 L! a1 M+ D& m3 ?2 C<4> Storage Engines+ j A. K9 T3 J* y( f& F5 I9 ~7 ^
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
& d/ U( c# h3 f" U1 v' c
3 p% m5 `' A. H5 d1 x+ X三: 源码分析6 ?' g" {/ _& K
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。/ y8 K! F( F: |* B
: ^, {. @: @" }( Z" T% H5 q
1. 了解mysql是如何启动监听的
4 v, M1 y* w5 M7 n8 t1 n1 L; @手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。* u3 j2 p- o5 j4 |$ z( c
![]()
8 n2 U/ r- F! Q# @# C( a, n! ~; u6 b& ]0 j- r0 g! h4 H; Q' l: ]6 p
3 C1 f. E. O. h1 {8 N# a% L
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。" L7 R( J3 x; ^& p2 ]
* p9 Y1 F3 z9 ?<1> mysqld_main 入口函数 => sql/main.cc- p( T3 f* _( K( b0 X; A
# S: `3 p5 n' }) W7 w) W! r6 V1 R) S% S5 [4 X* i5 U1 t. m
extern int mysqld_main(int argc, char **argv);& P7 J F8 C5 T/ C/ I/ G9 z+ ?
5 _, f$ r5 T" M$ i6 Xint main(int argc, char **argv)
: H& I+ m/ S$ D1 [( I% H! W{( }. P7 M% l1 B# M" n7 s y
return mysqld_main(argc, argv);. H8 k. c: ?1 U8 l# B9 w
}
3 `7 `9 ~/ n: O) D4 ~2 K9 w
6 @1 O6 r7 |! S3 V+ i( m
! Y" B* o" z/ Y' c8 _/ ?/ X6 A这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。. [' {$ u* ^4 [. x
" q+ Y. u% I, E8 r( H. x6 {, a3 ~
<2> 创建监听# E% \+ U( c6 f- E! _
; |/ s$ N* }' P4 d) n' z7 G2 s) E- {3 j3 x$ Q3 F+ |; \
int mysqld_main(int argc, char **argv)
2 i+ K) W5 I4 U9 a/ R& H{
. z7 N) ^. i: r- P8 X% d //创建服务监听线程
, X% ~8 Y& K1 L; ?5 m) g5 |0 I handle_connections_sockets();
, x, A, H8 ^# O; P- y4 w( _0 L" Z}
O) W' {$ m/ L }
9 A" n0 S' m* h* q# I7 N! jvoid handle_connections_sockets()
1 ?6 p# R D' \. H{
. X5 l: `$ }6 J& F; j7 b M3 I //监听连接
; T( D+ Z% k7 j6 f new_sock= mysql_socket_accept(key_socket_client_connection, sock,; ?$ Q0 W( f1 O b
(struct sockaddr *)(&cAddr), &length);& V+ \$ w9 J5 p+ a8 e* ]9 j
) Y+ Z4 n6 v7 M. u! B! P- w if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
% o6 M: c2 c9 m" Z thd->security_ctx->set_host((char*) my_localhost);9 z' G' p3 d# f; D4 Y
! y. u" P6 T- |9 i% \ //创建连接
+ r* r T5 X8 }6 }1 v) w( | create_new_thread(thd);' I2 [# f/ s: @0 h, d- D7 H. j- X
}
+ G5 N- a/ [- e7 M, s" m8 M: M. b" i& `" y+ Z5 J6 L
//创建新线程处理处理用户连接# L0 I* @9 e* a" Y# ^, E0 _
static void create_new_thread(THD *thd){# V7 y6 u/ n" W8 e" Z2 `
v1 u2 j# V: d) U
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
3 I% j: g7 q0 F# r5 @+ z5 n; C: }
3 t6 P( @4 S" X( L7 A( Z9 ~1 ~ //线程进了线程调度器9 Y: A7 x! L8 ]6 U* b
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); ) M* v, W# [% E% j
}
2 ^( u0 t. s* O5 K( L+ E2 ^8 g+ ^/ N3 H/ R# A
% O5 e1 M9 n/ g! m9 J H& d
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。& |- C& H$ d0 {3 ]3 \' N
6 a1 ]( F4 b/ m# z% G7 y4 N1 k( q7 z7 }3 M- U; n
2. 理解mysql是如何处理sql请求- `0 j5 _# k Z1 i/ e4 Q$ G
这里我以Insert操作为例稍微解剖下处理流程:
8 l+ l0 b% a) n0 }, i% v. e, |# M% e" p# o' C3 a7 v
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。8 f8 V# \4 Q7 n- B9 x
! Z$ w) g- P: e8 ^0 ]. m' R
) q4 [5 H" p( J* z6 \0 F& f
static scheduler_functions one_thread_per_connection_scheduler_functions=1 f( t$ j ?/ z; y
{% O* L9 d& D8 ]6 C; d3 ~: u
0, // max_threads6 s# S! f% ]) v. c% }
NULL, // init( |+ j8 j ]% ~: j1 w+ f
init_new_connection_handler_thread, // init_new_connection_thread7 |6 r9 k: |7 w/ J1 F
create_thread_to_handle_connection, // add_connection
2 e/ T1 G; z% ~, r( J0 t NULL, // thd_wait_begin( a2 M, Y3 B" F4 C; i# F
NULL, // thd_wait_end
9 q6 | {' t' C% _ x- w NULL, // post_kill_notification
& k5 ^" L5 }* n! ^ |+ t one_thread_per_connection_end, // end_thread
5 u, ], ` l/ J% R G M# C NULL, // end9 r2 }9 u: F6 u, l" }
};
$ B5 Q. O' H! M, @5 j$ q5 M( D8 }
) Y% N2 ]1 A& h) P& b6 e) O从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
" ^; W, f# ~4 E! y. u0 `7 x# g
+ d7 S+ w: w, }# Y8 M; S7 s<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪3 P4 ^# R' f0 Z; X4 B; @9 d
8 g7 L5 M: I8 \+ I3 s) R) B( I7 D- \void create_thread_to_handle_connection(THD *thd)
* H4 m1 P! B# w0 n' T8 q, C{5 |8 A$ _. S6 v5 }! U9 x7 y
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,% a/ X3 ~4 ]' f2 S0 J. @
handle_one_connection,(void*) thd))){}& _) [8 b& K, }; w u+ b
}
) c m7 g: o. y6 b$ _//触发回调函数 handle_one_connection9 l; M+ v- N4 M7 c
pthread_handler_t handle_one_connection(void *arg)0 l2 L9 x9 o5 @5 ~8 @; z0 @
{; v; F# P1 l2 q4 q
do_handle_one_connection(thd);* C2 L1 i/ V" X) `# c6 T, R+ q
}1 g3 B1 s- o; ~( v4 w+ y! d W) g
//继续处理
# E- X3 w2 Q' s9 {void do_handle_one_connection(THD *thd_arg){$ C, O, V' M/ u* L7 u
while (thd_is_connection_alive(thd))
4 Z$ U9 Z8 L+ G! V8 Q {
; ^. A0 L: n) O$ L' r mysql_audit_release(thd);9 u2 C6 h9 H9 U, ?: s
if (do_command(thd)) break; //这里的 do_command 继续处理
) p/ D8 \) G( o7 r- W* Y: }5 E }
1 N* H. b' j' x4 q}$ I0 p0 P( I/ E j9 {
//继续分发/ ?3 s% A& i6 E
bool do_command(THD *thd)" P8 R2 Y, m% `6 o. X k4 Y0 i5 C
{
, p1 M" g- D8 E7 S: ?4 Z6 W( W5 a return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
) P, v" [- i. F2 Q}
. [- M& H3 L: ]8 K: P/ x6 Ubool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
% c$ a9 A5 e/ B! i5 y1 z{% n; M9 i$ q* ?
switch (command) {! X( g& l. c+ S1 b0 H) x
case COM_INIT_DB: .... break;/ d9 L; P! r9 t& z5 f
...; H0 t ~6 X Q
case COM_QUERY: //查询语句: insert xxxx
; c- s) s7 z L* j* p mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析( b: t& m y j' l) e4 W+ T/ _! c
break;
2 Q" x4 A5 s3 j }
& D& y) w) N/ g5 F D4 B/ J}2 ~, _" o, ]2 t( H' D0 K8 Y w3 v
//sql解析模块
' q& u2 H) m/ ^2 K1 E. J1 avoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)% B& F; d7 V( n5 R! A+ d" h9 a
{
; c2 m* b1 n) C* R' c error= mysql_execute_command(thd);0 A/ C, ~5 J. b5 `* M7 E9 u
}1 U% U. o! j1 G) c
. z. F) s+ P3 W- n5 F
7 i7 E3 w% U9 k. @) M<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
+ K; W/ k7 t4 P, p: S& r
( W% ?# T0 u8 s//继续执行
2 L( r+ M. _) y- I* Z. T$ S, ^int mysql_execute_command(THD *thd)9 x( M3 X( R5 X( F8 q& ]
{: Z% @; L0 m# Y! a3 E) T; d
switch (lex->sql_command) ! d& S5 B" a2 E) b
{
$ ~7 u/ w9 ^0 J case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
l5 u% U0 W3 a7 Q
8 s; K2 Z! N9 } {+ K# l$ k; J, }4 g //这个 insert 就是我要追的
0 w9 K5 e, q. a case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
" _$ F" T1 Q3 U9 ~ lex->update_list, lex->value_list,
0 X2 t' D0 ~. s0 y" @1 i lex->duplicates, lex->ignore);8 I' w, p @( \7 L" U+ x6 w
}
8 ]2 p. Y8 } y% ^& Z}' M7 E) i. f# M# D# [2 n, b
//insert插入操作处理5 V3 j4 y( p {$ D
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
& q( S$ k/ D$ P. ^* e List<Item> &update_fields, List<Item> &update_values, 9 v! |/ {5 v6 a' Y ]$ `$ X
enum_duplicates duplic, bool ignore)# n! Y6 a, u2 Q" ]! q
{+ G7 A4 U5 I; d6 \
while ((values= its++))( `4 k; P9 U9 Z
{2 j9 H! C7 L6 s0 n5 w# A9 ^
error= write_record(thd, table, &info, &update);" b/ h3 o& Q* S8 g. w
}
3 x, f* X6 |7 m+ d) w}
0 b, v* b; h9 x8 W6 m/ F//写入记录, F t4 Y8 G6 z& P( |* J& h6 Q
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)* t6 i, k4 [7 G
{2 ?- _ A: F. ?+ D" G! g) Z4 m
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
, h) A( c$ A0 n' e- X6 d/ j {8 u. q9 y6 X; E3 V! R/ x
// ha_write_row 重点是这个函数
$ T* H. y% D$ h$ K1 C while ((error=table->file->ha_write_row(table->record[0])))/ B* Q. j% \/ C- C" {8 k1 n
{
+ ]: J* \0 k; E* e% ?, V+ W# S4 o- E ....6 r* Q1 o4 F; t+ E
}
9 J9 B u% Z9 L# v }" L! P9 e" p1 S6 F& y F5 f
}, U/ S8 n/ q# \* I! t1 F
8 p( R7 [1 y6 s4 u1 N- Q6 E
6 d! O* W, |( T) q! d
7 ^% Q% b* _* N5 d
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
- \+ Q/ w, w8 Z! o& r, I0 Z3 T1 i6 l8 U. Z6 s
<3> 继续挖 ha_write_row
$ S/ j! V; g7 p/ _+ z
; I! i$ r9 Y# m4 C0 F/ }1 bint handler::ha_write_row(uchar *buf)
9 }( B' ]. ]* u- J{
% ^" l4 \, X% a" \6 F! d MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })* r4 o$ `; f0 W, \
}
" r( w$ R: A( z r M' c9 s( ?9 }1 z, g3 o% Q6 U0 l
//这是一个虚方法
9 g4 I+ q- o" |, R* `! l0 t! svirtual int write_row(uchar *buf __attribute__((unused)))( o: a1 R( ?/ _3 A k; @
{
0 u: p# G0 N: Q7 |) o return HA_ERR_WRONG_COMMAND;
) J: s* s& Y5 g0 c+ }0 u}
: W% v7 o1 N/ q g5 l* H! l, S8 r$ X/ m, r0 h1 \; ^
& _ Y: v2 Q+ ~' G5 I
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁5 P" i" q; t# j2 O# \
& b9 K. U3 A* u7 @; R9 A& c2 F x
3. 调用链图
5 Q* l) ^0 d/ h/ p这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
# V4 J) R a" _7 \$ k8 A ' \% c9 a5 E! D3 I: E: g) u
% Z& J* [1 C9 y. R2 x
3 k* V+ R" E1 @' m; k- v& d三:总结4 x" l. g% I. f! K$ S, p$ f
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。% y- E% K4 b0 d5 P8 q$ i
————————————————
& W" q; T V% ]+ r. B$ S. B1 C版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
) s# s3 w/ U0 i$ l2 s. G2 i; G: D原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415, @; s) d1 C8 _5 y: k
|
zan
|