- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55501 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17602
- 相册
- 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 田老师国赛冲刺课 |
一:背景
& {3 Q/ o! P& p7 k1. 讲故事/ T8 o& n3 F5 U
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
) Y0 ?# z& k! W6 V$ ~/ t; T& I/ m$ q- S' r# p& v
二:了解架构图
0 O# h* s- ?. w$ Zmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。5 o2 ^6 ^1 o. C9 D% B; O. Y- ?
' h2 g( g* ^ s( U6 |
1. 从架构图入手- r9 | E2 D. {: t/ @* \
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
( ^& ?6 \3 ^$ H0 D9 i; Z+ X 0 ?7 i! `' \8 {0 M! ~- ^0 I! m
0 b7 T5 ~: }5 {% v% V: J- b. {9 x1 Q" X
( |: p! K0 e# \: C1 j) d其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
: h1 `' D" W0 ^$ U8 Z. R1 y6 U1 n2 x" \& h% s8 Z
2. 功能点介绍
: L# z4 L7 h+ u- T0 A' k1 tMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。, I4 P" g% g4 p& E; L5 A* p
! g+ c! E# q! ~2 p+ {4 X1 ]% I
<1> Client
, V$ O2 H& }' ~8 Q% C# m不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
' s3 _2 v. l5 Y6 O0 |) ?9 |: l+ E4 a1 m3 U9 Y$ ]. p
<2> Connection/Thread Pool
' J- w) l3 S5 K% gMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。* Z2 C$ O7 Z4 I
4 C5 s5 g y4 j' C<3> SqlInterface,Parse,Optimizer,Cache
' ]) o3 F5 W& b对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
" x+ d2 R5 ?; Z+ U- v
3 d( X- g# \8 [! f: W- u<4> Storage Engines0 A# a# x2 R* w$ o
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
% z5 S( g! l$ @5 r& n) p8 b
" u- q! S, u: [. |( Z. S3 \/ ?三: 源码分析' x3 V3 t- T' ^9 ~
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。( s$ \- W8 p& u }
) N4 s8 M: Z1 u9 A1. 了解mysql是如何启动监听的
7 Y3 n, i d& J+ n手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。1 x" U, b2 b4 \/ V
![]()
# `3 N3 O: O/ O8 i
4 C" U- J; J6 s9 z g6 G% S+ c1 D2 `. k3 t6 q$ V
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
( B3 k. W" y9 ~" h+ M
3 o8 \0 {2 P, n* d5 a2 G$ p<1> mysqld_main 入口函数 => sql/main.cc
2 ` v% I4 l$ A1 \+ t0 |, q M7 B O. L3 H$ b; N+ d j
9 `+ [2 W7 s* E+ z" I& Oextern int mysqld_main(int argc, char **argv);
/ o/ Q, ?; L ?% g- e, Y
5 Y' z0 E: }) Nint main(int argc, char **argv)
% g7 w6 j0 o$ n+ z ]{
x2 j" A* o5 C& K. M8 y return mysqld_main(argc, argv);9 r' z! x4 d1 _! h
}
6 I' N0 }& Y/ ?, d8 l7 } q9 r, r$ M& A( ^
* i- Q; o+ M& A0 d4 @' K这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
( C2 D5 n& |8 w
( J2 Q0 K0 C. J$ `6 o$ x<2> 创建监听
: Q8 t% Y5 H* u) t: a6 A
+ V1 H6 W3 t3 l
+ a; E( W: ?. \) m+ c+ Iint mysqld_main(int argc, char **argv)4 n! H y% O: n" C
{
8 e, G7 w8 V, @; E( x) P //创建服务监听线程/ O2 k9 B- |& t- D1 R/ x+ k" a' I& _
handle_connections_sockets();
. {9 p% i7 Z: F4 s$ W}: ?6 C6 g I8 E; K. s8 S
0 g% s' q( L; c' n- F2 B9 Wvoid handle_connections_sockets()
- P* o. \* Y% q, g4 E! ?{
/ e# w" D' \+ N) { //监听连接+ Q* `9 y2 [* }0 B7 c" n3 w2 t" M
new_sock= mysql_socket_accept(key_socket_client_connection, sock,2 o/ E8 w; {) Q: C/ R4 z
(struct sockaddr *)(&cAddr), &length);
( d9 c- ^3 X+ E: ]6 ^4 I& o7 ?; D. j/ }" q* ?- r. X4 F& D: X
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))+ V B8 s- F, b0 D, p) |4 W, X8 S
thd->security_ctx->set_host((char*) my_localhost);3 X) u( Z- j- d7 x3 O
$ [: \# t* t# r- o( ~# a% r1 u
//创建连接) H' ^3 Z- N& ]- Q
create_new_thread(thd);
* b+ c: l, t8 o' @4 w2 y, `}( y0 F! G6 Q4 A' ?4 ?
& n! H Y9 f J' c( o% ^//创建新线程处理处理用户连接
0 d# R* ^$ W2 A9 Tstatic void create_new_thread(THD *thd){; |% G/ \6 ^( |& ~- n* Z
; ^& e2 h5 i1 q! f- H- a
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;5 ~) a# ^6 }2 W1 t3 X, O5 r0 u
" w8 g& e5 p E# } //线程进了线程调度器 t* q0 v" `$ E- p" I: \1 b8 R
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
0 j; J0 Q# @% d- H' I c3 G8 L5 r}
* E' \4 Z: G8 B* i- w5 i) X
' a& _0 J) R* B/ q# s
+ Q1 a4 r( Y6 M7 Z/ ?+ F/ M至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
& G& E0 [, A: h5 h+ X }, J G4 z
: C- Q. X; l* C- o. l9 S1 }( E2. 理解mysql是如何处理sql请求# K' q5 G7 H1 x" V. p2 @5 ~, v
这里我以Insert操作为例稍微解剖下处理流程:
+ Y/ s) Z3 ~* \/ N$ Y( r3 j! S
" J+ I) f# L6 @3 }3 n当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
7 V2 w4 z- q0 b; A5 Q
* M( E. k2 Z/ O& H/ `0 G
+ m8 y4 v+ f! w, k/ Istatic scheduler_functions one_thread_per_connection_scheduler_functions=
+ X9 }5 m$ `6 R, I* T) b{2 I1 G" I& w0 f( i, u
0, // max_threads
$ v/ |3 S: k2 U7 T' q; K NULL, // init P6 S8 ?( b/ }: t& l' a
init_new_connection_handler_thread, // init_new_connection_thread8 S( Q. m& }3 k& x6 X+ j! [
create_thread_to_handle_connection, // add_connection
; ~- `* V, n7 Q. G( m7 b NULL, // thd_wait_begin" S% o8 C. J0 J
NULL, // thd_wait_end
' x- y$ v" d ?; B0 X6 B NULL, // post_kill_notification
4 a) I0 }% O3 a- T* ?1 d one_thread_per_connection_end, // end_thread( h' r( Q4 _5 z: N
NULL, // end
% D4 W+ d9 x9 c. a};
6 \& }0 y( Y$ b( W5 z3 E/ g! s" M' m$ B
* q* l( |7 G. O9 Q- j, j+ x从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。, V ~ z- @9 @& w5 a# m
; h. `9 H. h- D. w<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪' M5 W! C- C' N% b: e5 M9 T' b
% W- f0 n% S/ A( x1 l% `
void create_thread_to_handle_connection(THD *thd) c0 Z" ?1 C! A) u7 k8 v
{2 @# Y8 s' k8 o0 K7 F* D
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,8 S b/ R( F0 V5 `
handle_one_connection,(void*) thd))){}
x; G# M: S# \& ~& [}- U |: r; u. a# H' {
//触发回调函数 handle_one_connection% Z6 ^! m% m4 N/ l. ]
pthread_handler_t handle_one_connection(void *arg)
( L% x! O- d$ Z# c{
7 Y7 p/ ^5 e4 o; D* |4 \9 r do_handle_one_connection(thd);1 u) y+ v& X$ N2 x* ^& E
}
% H. R6 x" j t1 `5 L//继续处理/ U' V" s6 d, F! q4 C. @
void do_handle_one_connection(THD *thd_arg){6 U2 v* ]1 s6 @/ w% Y, X4 p6 Z" T
while (thd_is_connection_alive(thd))
+ E* F1 x$ g: G! c6 k {
. H0 L* K; G0 g; y mysql_audit_release(thd);
, _0 ?3 o$ u! G4 a& v$ `. C if (do_command(thd)) break; //这里的 do_command 继续处理
6 Y8 t+ V- n6 g3 \6 u# c8 [: B }
- O5 g& v0 B3 b, m( B) d# u1 r}
+ X1 n( k v8 V8 \$ _//继续分发
5 i( i u$ j+ t' B4 z: xbool do_command(THD *thd)
; ^7 {3 e% S& f{4 J! C9 g) Q& d0 F) E6 C
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));3 @6 ~1 a# J, y2 l
}
4 ~4 ?% W( |3 e* M- T/ |: }# Y9 cbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)% K' \! i8 r: T6 C" k: I/ B* a
{
- f7 `$ q/ p0 H switch (command) {/ p @. D+ g7 S* C! Z
case COM_INIT_DB: .... break;* F- N+ Z6 G5 T
...
6 G5 `; l/ R3 q) ]) M case COM_QUERY: //查询语句: insert xxxx; Y% ^& z2 I% m$ V, i
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析+ e5 z1 ^: i- ~6 a7 `" _
break;: k. A7 u# e- @
}
5 s1 U: X' ^- z1 _. l! o. Q}
) ~9 d' T e/ A5 r; r8 q5 K//sql解析模块# u- ~5 S b! y# f' v" l/ T
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)( Q( \& e: {* J0 w+ i$ w" I4 W
{
4 z4 i9 ^: f' T/ k9 W$ c error= mysql_execute_command(thd);/ s+ V% P+ _ `7 n/ `* G
}
" p, m3 a* d5 w" H# o0 d( c3 e
8 y# g/ g: \( U( t5 O% B& C- n
8 ]' |) t, d$ h& }' x+ u. n<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。3 K4 m4 O$ P4 w7 L+ ]
( m; s9 y' a' o//继续执行. G3 I! d9 |; u* h2 \! C
int mysql_execute_command(THD *thd)& T8 O9 P, `% o% n6 H$ c! F
{
/ R/ W6 `; `6 c4 ] switch (lex->sql_command) " \. X1 B4 {% b0 e0 L$ s' j5 V- \5 G
{ Q- R7 W4 y7 I. B! y6 d
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;0 ^$ F" f+ y9 R* M" n! T/ J: L& B5 C; H
$ a5 X9 K0 P5 M+ V# U0 K) J //这个 insert 就是我要追的( _/ t1 c r1 Q' M! i' V
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,) @: Q5 Z, H) e/ B
lex->update_list, lex->value_list,
* h; m S7 T- C5 n1 O+ V, V lex->duplicates, lex->ignore);: p0 a. z, T* {7 ]: l4 m
}
) x* N# \) F; @8 L}
( M. h. T- _# e) x( J4 A! J& s//insert插入操作处理
! D- Q" l9 I# w# S8 e" ~bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
. W" P; y2 n$ Y List<Item> &update_fields, List<Item> &update_values, # i5 m8 R2 Z6 D1 e& O& C6 o( l4 O
enum_duplicates duplic, bool ignore)3 N$ B H4 |6 }9 `0 a6 ^. I
{% M# w! g3 h5 K+ s# Z7 T6 }. d
while ((values= its++))
& \4 ?% ^: C% m+ a" `: u% o/ q) Q {
0 P, m5 q0 v8 E6 x' B2 T' `9 C error= write_record(thd, table, &info, &update);5 j1 l4 n7 |; a) ^ H
}
, W, A: x* t' `3 T5 p1 a} A% B) |% W7 j6 Y- l9 L- U
//写入记录
, @+ i/ d0 {3 F& {6 ?int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
. \) @1 K) l# g& V4 L' P7 R{* |/ F+ ?) ^- J4 |5 e$ Q6 a. }
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
$ [" k. i6 g' u/ H( G" Z {9 |3 E% q8 P6 ~" K
// ha_write_row 重点是这个函数+ A# I0 J/ c4 H
while ((error=table->file->ha_write_row(table->record[0])))
) s; Z8 J. r. X' b& s$ j0 Y {2 G4 ~6 q g. R
....
7 y5 ?% X- _* F5 ` }
5 ]6 e l' i' I1 Q# U6 g }. b7 n0 P- j$ w# ^6 [
}
$ D: E, w5 V3 w9 {4 b9 g' G$ ^; Q6 x
4 P5 Q- f9 j6 f; [8 ? _& p; I# H9 j6 X
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
$ J8 B @) B+ S! ^( R5 \% r' B1 |/ E' Y. b9 @6 N) x0 I% ?
<3> 继续挖 ha_write_row
/ @/ T' |6 U& R8 D, e3 B& | @% `1 u% O2 o/ D: i
int handler::ha_write_row(uchar *buf)1 S. f! j4 B6 p {3 n8 `) N
{
. P( p: {) {% E& o x9 } MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })5 h3 u. |* w+ ~) |. n
}
& @6 n9 B$ { [$ r2 ~" c9 ~5 L* E* i2 ?2 ?
//这是一个虚方法
4 B% E. |6 J0 B }( Y! jvirtual int write_row(uchar *buf __attribute__((unused)))
5 m: u: I: E+ U{
, t% J* P1 s$ Y- L. h return HA_ERR_WRONG_COMMAND;
" Y& s/ w5 @! k}# t2 `3 t$ R5 V* m; Z
3 `" f. Z3 P3 k
0 r/ z( U; I: ?# G* N5 n& T看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁' O: @7 s% i$ }& ]
; R! n, v) _4 Q
3. 调用链图
+ G/ }1 T H- V( u这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。8 B/ q; z/ Z3 x. u- ]1 J
![]()
' @; _. F4 t7 I, S$ ^ I- i1 c% o" |# j6 G5 e
3 n$ A7 @0 n' A8 k
三:总结
4 x$ u5 T$ {0 w0 W [7 U大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
Z; ~2 v) y( x# V9 e2 i) D————————————————
6 j) ~. I" `& V, S& x+ c版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
3 {* I+ o, }! i8 @) n N原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874154 k) L2 S1 J, B! e, B A
|
zan
|