- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55580 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17625
- 相册
- 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 田老师国赛冲刺课 |
一:背景5 I9 E4 W+ _! j2 H
1. 讲故事- f. E0 d% R0 U# |
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
0 p N5 P1 _! B7 ~ p! |9 C) n$ x8 d- O: E% N( `5 X- Y
二:了解架构图
8 ~ o% o: P: J7 W1 ?mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
1 Z, |1 b3 \, Q! ?( y& b- {4 u3 {2 e( M) k: g, G/ M% x+ y {
1. 从架构图入手! a' D/ c5 [" @2 V" F/ g4 {0 X
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。* k- ~5 H7 }' _4 ^ I
![]()
6 z+ r" A# \" `) l3 W! H X$ k4 I, o2 i( H% `4 a4 W5 g; [$ J. }
! B' F3 V1 k( F5 D6 G' |
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~+ L! J, n. d! J& }7 q# p7 x$ _ s
( n* a" v2 z5 D; j( C1 \
2. 功能点介绍) c7 A3 l' f& h
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。7 ]( K: x* z, k, ?) x
) G0 i+ K' z) Q- x4 u" W0 @
<1> Client
. q) A, v( L1 e0 a+ |; P不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
$ L! s Q3 M) v( h
' c8 L+ m- Z( F# B+ A<2> Connection/Thread Pool2 Z# u9 p2 ^3 g, \8 b5 Y- f8 ~1 X
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。. e3 Y% M) a9 ^% `4 S. k6 V
1 p7 s7 \4 y- i- V1 C4 m) {
<3> SqlInterface,Parse,Optimizer,Cache3 q- d# ~& E* B6 i
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
# K4 O1 Q2 F8 B8 g: ~+ D! Q5 A* a" @2 {+ c
<4> Storage Engines$ u" w& l$ ~/ \5 t. W
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。) P1 P6 D; d7 U8 y3 |, w( `
" _& L6 _! {! f三: 源码分析
1 G. f8 {3 Y. B1 v7 r0 `关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
! q/ {7 U- ^, l: M9 a
2 s, w" m( G& e1. 了解mysql是如何启动监听的* v1 I- e- |- ~% w% U
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。: `6 v; M2 |* c \; E. l- B
![]()
6 _" ]. M! E$ |- Q# W9 w5 { H+ H3 b- T3 ^# D5 o5 G* a
7 R1 q8 h( M& G0 m- O! x. ~从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
1 y t3 q; b5 k7 I$ S, P$ f* q0 p' o0 @; r5 @& y+ Z& ]
<1> mysqld_main 入口函数 => sql/main.cc
3 e0 n5 v) c: w+ O+ X
' T1 E5 B, E% R. \1 C9 l
6 \$ K: Q" v& k4 I* k5 T4 D' Mextern int mysqld_main(int argc, char **argv);
; O4 E: U( [/ ^4 V
/ I" v9 n a% Q8 t2 a' Vint main(int argc, char **argv)' l9 @4 S" S% N# u' w
{
$ U3 D- g0 S {' g8 W5 Q return mysqld_main(argc, argv);
" g+ F# b. M* l4 G# X}
. [" c7 v7 C7 z3 F# |* m, _: z1 }7 u
1 t( f8 s S6 o- `/ W; H0 p8 t( R/ ^3 g, T$ p6 W
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
6 O. T# ~; B* M/ g" `( f
: G* R( O% ?. W. N2 e6 ^<2> 创建监听 i5 Y9 R1 |3 B3 e
& p* i' v; ]2 _1 s4 ^& Q6 \2 {$ n8 Z6 K
int mysqld_main(int argc, char **argv)- ?9 l: Y# E8 O P7 w
{8 s: }. v ]4 O1 Z3 w' q
//创建服务监听线程
2 T2 p, A5 D6 Y$ T6 D4 n" ]5 F. Q' } handle_connections_sockets();! r" G& L( P7 k8 e- g( t! `
}. m% e! N8 ^6 W- n ^
# z+ p2 |% p: j( t3 Svoid handle_connections_sockets()
: k2 c) N2 D7 D' @{
+ Q. D: v5 f8 x) b( k //监听连接
/ @: h f! }0 R: a new_sock= mysql_socket_accept(key_socket_client_connection, sock,6 @! y+ w3 J; N
(struct sockaddr *)(&cAddr), &length);
5 _; P, W( h& z5 Y3 D' |* K! _
- Z6 @4 ?' e( z if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
1 n5 `$ S* A0 ~& \& ^' w, Z" D thd->security_ctx->set_host((char*) my_localhost);& X4 q: F1 }2 _9 P |
4 E4 c3 A7 R7 W2 [4 O$ X3 m //创建连接
5 c3 ^/ t# v8 D2 R O create_new_thread(thd);
% \% e/ N3 V% W0 T: W}6 L3 O0 K$ n/ \: s
6 _ p* i! P9 G# u: u7 U& S/ s
//创建新线程处理处理用户连接
) @$ z! ]" ? |0 O( @! K' J* i1 S* astatic void create_new_thread(THD *thd){
6 d+ q" m) [4 i& G, x! m) M s3 t1 b2 r0 F% K
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
* C* E- Z" X4 u& A0 I L1 m
5 G) O0 c' Y S; z+ S7 y4 h( C, h //线程进了线程调度器
2 ]% B0 s9 m1 a( m MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); 4 x. ?" f' a) q* G* c$ \
}! K, }' q' l7 _% k* k- D' u" Y1 X
: k- q$ W4 P. y! Y5 k6 ?3 [
/ Z# \# j$ M; ] K4 ~# r至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。3 P* I2 T. x& s# O
- S+ e" X+ F/ R; D* c
0 E$ i8 v3 n5 ]2. 理解mysql是如何处理sql请求& g* R+ O& I7 C+ a, P9 q* ]- O
这里我以Insert操作为例稍微解剖下处理流程:
% A, @9 ^6 K5 [( }& S/ ]
; w: K* \2 r' v0 H当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。/ a. D/ K, ?/ e
! I% w8 i. j: n3 s6 ^0 S; ?
& ]: `& o5 s) c7 A% [static scheduler_functions one_thread_per_connection_scheduler_functions=# p% |: H5 m" P
{
% R5 c) ?9 n, i) a 0, // max_threads
8 j* |8 r9 W* w# I; n7 x5 U NULL, // init
a4 q3 U w) K8 \ init_new_connection_handler_thread, // init_new_connection_thread
8 D4 O# i- Q# J0 n/ `, Q create_thread_to_handle_connection, // add_connection
8 \6 b) b* q- V0 P( Q) i {. A( W NULL, // thd_wait_begin
6 w+ n4 l# r6 D3 ] NULL, // thd_wait_end
% {6 Z7 I/ e3 R1 w; n+ D. u0 G& f- U NULL, // post_kill_notification, y' ^( z' m6 C9 ~; ~. w
one_thread_per_connection_end, // end_thread
1 G5 S) n2 D* D! ], r NULL, // end7 ^- w; D- o/ D, R/ q9 O
};+ K& i1 l0 d8 h1 f4 z/ C2 x7 T! C
3 L6 R2 v' v) F- s3 h$ h! N( H
& k8 f# r" @. D从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
' |; F; C0 Q: V- F* Y: B, B# S& ]$ ^( K6 r: }5 }, R. W
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪" d6 ?/ P8 R# P, Z
+ l0 i1 b# I6 M, G5 Z
void create_thread_to_handle_connection(THD *thd)" ^* V( C2 ~+ i6 @2 p
{/ x% g T; U( E* l- P5 A
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,5 u1 w$ D/ B( T) u: R! D
handle_one_connection,(void*) thd))){}6 p! l8 X! {+ W; _" l5 _
}
" `2 H7 }8 n( i//触发回调函数 handle_one_connection
N; f2 N2 q+ r& b- Kpthread_handler_t handle_one_connection(void *arg)
% v% W2 I/ l5 m! C, A8 b1 i n7 A{
/ Y+ u, r; G1 H% M1 T do_handle_one_connection(thd);6 Q; r1 \) B2 | j# b. R3 G; S
}/ u6 o! i/ L9 l
//继续处理
& e; F% R1 c$ evoid do_handle_one_connection(THD *thd_arg){: _ M D4 U+ j( v9 U
while (thd_is_connection_alive(thd))/ ^9 }7 h+ F9 Z+ }' C( j$ Z
{
/ N: o8 w/ }- Q mysql_audit_release(thd);9 t0 m3 X, U! [$ L" m
if (do_command(thd)) break; //这里的 do_command 继续处理
9 r8 o) q2 h# l3 J4 S }+ P1 E3 L* \% j! O' x
}0 c( r% v1 A' @9 _8 o+ m) ]
//继续分发; a/ L( U' K7 t9 D/ U5 t0 r2 z
bool do_command(THD *thd)5 q/ i3 m# B$ g) R) G% ~3 D5 f" z
{
; V" A$ e8 X0 u& p" U1 z return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));' L/ P, c8 Z ~4 W
}; `% \0 h& v+ b
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)( A, T( J7 a& H/ N' A, j0 {
{3 O2 B" i+ v' {" L
switch (command) { q1 `) f7 x/ s+ E
case COM_INIT_DB: .... break;
/ c/ l0 p8 t. p& } ...3 |. v; V2 a1 v) _1 v& [0 ?
case COM_QUERY: //查询语句: insert xxxx
+ O) I! Q; |9 K9 I4 I7 D% @: |5 \ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
: I% d$ I" |$ [) [ break;; ~/ X- ~3 w, `+ v
}% D1 T5 ~2 @. n% e- L2 |% O7 H" q
}$ C4 n: Q, N) o
//sql解析模块
; v" a9 V+ v! n/ R" F' v: ovoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state), Y9 X7 M W4 Q7 V/ X9 Z7 d, }
{+ g: w) E* b* Z
error= mysql_execute_command(thd);
3 Y! p% L Y8 Q* d* M1 _; O( l}. t& S* d- C5 a* O" C4 w
+ E% M% Q; X1 a% u
- s( }" I* g5 y+ T) }; ^<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
3 \* E$ r5 _% W5 O$ ?! ~) q& ^( X0 {" g3 W! E
//继续执行
' C$ K3 t6 G# ]5 zint mysql_execute_command(THD *thd); _, z! x2 S T/ _. ^3 u% o4 E) [9 K
{
& {7 }, c( l8 L& o& [ switch (lex->sql_command)
1 s, ]% ?; u/ l( d) s: W {
/ L6 R I7 v9 |+ s8 J$ @ case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;6 r+ D" {0 e" [% {. Q
! G3 R$ \( A8 }0 C //这个 insert 就是我要追的4 [- B6 ?9 C% p3 V( o% _
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
' P" f$ m7 ]& V m lex->update_list, lex->value_list,
) n6 |) b! ?8 C# q: | lex->duplicates, lex->ignore);
: w# R) l7 A" O }
/ B4 X: A/ ?6 d. g( F! N' K4 u" c. D; I}
( b& s; S, a) k: W//insert插入操作处理- W. ^9 t) k8 Q( ?* R- `3 P4 u) O
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
* q6 U' ~4 y0 h7 w List<Item> &update_fields, List<Item> &update_values, , O! h' {- m" F6 d' D: z( B8 T1 |
enum_duplicates duplic, bool ignore)
% Q% \$ o, p8 L( _- q- u{
: p: H$ s% M L7 l% E' r while ((values= its++))
9 ?$ ]; ?/ p( F1 } {
" U/ |1 H$ o, G/ S error= write_record(thd, table, &info, &update);* v& y7 H5 ?3 T2 i2 [7 D
}
7 J+ S5 j: n% Z3 o7 r1 N4 I' a}4 G1 g Q8 M$ I, z; {2 d& Y
//写入记录
4 h% c, ]6 x. V4 i' sint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
$ L$ e6 Y o2 \{) K0 P. A$ ?$ H. @+ D! H# c
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)' Y& @8 }) S% _; M1 e8 \
{
6 W- g5 s2 V9 E' i4 u // ha_write_row 重点是这个函数 p; [- C' s9 {! I7 b
while ((error=table->file->ha_write_row(table->record[0])))1 s4 y" N4 w) C( h Z
{
" t1 }5 c7 w X5 `; C5 \* \ E' H ....
: W/ r1 [' q/ g( J. K$ F }) P5 k0 R" R: A7 ~% J9 j
}
. ^+ _7 X, d+ h! S}' D/ }5 n# l$ I4 Z
2 I$ k. D# h* d3 ~, ?" D5 i* P
3 r/ d" L& m* Y2 H$ ]/ t( S6 i/ S# i' M2 L9 K8 H8 N5 z! C
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
; c4 [( z; L9 a- y9 G7 i3 y7 \# d9 o) z ]4 p J' C J
<3> 继续挖 ha_write_row
% J8 o# }$ A+ c) e: c: U1 w8 A( C: ?
int handler::ha_write_row(uchar *buf)
! I/ @2 o. J! {" i( k{
, H! q3 H( O) N) z3 t8 |$ h MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })( l3 p+ Y( r. z. z
}
3 `$ y, \( v% E/ V: {
' i! ~3 F2 t" B" X* @//这是一个虚方法( o! p$ C/ \$ D; r( d
virtual int write_row(uchar *buf __attribute__((unused)))7 R( M. j0 A* h; S& O
{ w# B, T5 x: F& i9 Q; o
return HA_ERR_WRONG_COMMAND;$ P0 c/ I$ k- b- h
}# Y4 q, ~) t2 G5 k
) F% y3 g3 ^8 g) j6 F. u" B5 @2 k- \! n+ W# A) S& F9 V$ v
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
2 M @, c) h( r3 b& k. D' z1 u' o9 F- x% ]+ W7 |7 S7 i/ X4 U( r I
3. 调用链图5 t- p/ y5 ?* ^( a
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。! e$ Q: c% g' D" `$ `' K Z( M
![]()
( W* G5 t. K e- q6 Q- h8 U8 d7 O* s3 B! \
7 O' G; ?6 Z& F x( c8 {: n三:总结+ |7 T# R j# H$ U, @* v
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。. ? v5 D7 E! o8 E0 s9 {
————————————————
) b0 D8 l4 z3 J* y" m& o版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
0 p" K' k& m5 ^; _9 t' U原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
) ]( k$ w) R# [) Z, V. x6 F |
zan
|