- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55526 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17609
- 相册
- 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 田老师国赛冲刺课 |
一:背景
+ i4 t# ^/ E( e1. 讲故事
& @8 ~. I8 _, Q0 V最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
# a$ s. x: y7 ?( W6 O" F. `8 C
7 A3 S4 ~4 l7 ~二:了解架构图
2 i6 l- J9 ~- }& d, Cmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。) r7 o0 ]. ^% v. Y. A7 n4 ?% `, e
3 `8 _( O1 L8 D4 u" o( t1. 从架构图入手7 m; `& y( B' T7 j, w& a# N6 Q- F$ I9 c
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。, u) i5 ^. Y6 ?' B/ |0 D
8 C, l d; F' [6 Z% S
3 `' M6 t/ _( a, G* Z2 B {8 l
, t$ v" m* ?: J% { n
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~0 ]2 w. G8 Z5 s L! }5 ^! F
% \' s3 t( F" b/ @# P2. 功能点介绍
: l G5 P$ W F: J ~; EMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。% t- u/ O- m' U1 c- _) o% R) S$ g
7 m$ t) b' K8 @( N9 q0 s9 n
<1> Client
6 J- @8 z2 C: G l1 Z5 v8 i' D不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
+ O% T- Q0 [ L
3 W0 C/ s7 Z( h5 N7 V8 t( v- \<2> Connection/Thread Pool, g- z8 J7 K& C: C+ q6 _% C
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。6 J. P& r# h' i, }! X" g
' y# M' i0 h5 u! |# d<3> SqlInterface,Parse,Optimizer,Cache9 p3 A/ S: d" d6 i/ ]2 l2 {
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
8 I' A: \1 f! _2 C! A
; I5 w# x' V+ }: y<4> Storage Engines
, m0 W1 w8 P* ^5 {8 t) V负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。 P6 a& {9 `; }9 h. Z( A
/ S* t- C, Y1 E( |' l
三: 源码分析
) p6 Z- ]6 ^* P. W; X/ P关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
' E" q+ a$ A, O3 C8 K
+ t6 W& k6 I7 J+ q: V# i1. 了解mysql是如何启动监听的6 }4 X( d# g) f+ e
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
: I+ X2 z4 X- N+ O! B9 \' Y* d4 ` 9 X/ u6 _4 S* j. _5 @- N2 h
, c2 R3 V4 S; ~
; s9 ]; ?+ b9 Q& d从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
* h9 c1 W# s3 q2 m3 g
5 @/ b& A5 m5 |* B<1> mysqld_main 入口函数 => sql/main.cc
: |5 l2 K N! V4 {* b! X1 {- @- t! Q+ u
% \- g+ D4 ~$ P4 ]! Q, z
extern int mysqld_main(int argc, char **argv);) y1 k3 S& g& G Q( ^
6 G0 t# j. M' ]9 g* \8 {' Lint main(int argc, char **argv)
( s% Y+ X9 f3 E! _- k{
* x" z8 f$ c/ ?4 m return mysqld_main(argc, argv);' J! P5 W3 x, B8 F
}
5 _' L8 x; A7 K$ h4 M" L' ?! q3 L
8 C* n S3 b7 [; }/ L6 Y$ O- U3 ?1 F这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
$ Q$ y4 V; W5 Q! N; b# Z2 @
- S9 U( N9 c* _% E a; G N<2> 创建监听. h" X E% P- Z& L: f7 N6 y4 k
# ~: D3 g! x$ G$ q, Y; ~1 P& N
) w3 {8 o* l, M. Q% r" f% }
int mysqld_main(int argc, char **argv)
6 U, e4 c! {3 `8 s$ ^- Q& U2 E{1 m+ d8 d- r3 Q& ~# W- F. _5 z
//创建服务监听线程
4 U! @# ^! X7 E/ E4 A; w% y( m handle_connections_sockets();6 ~3 u6 j; L. U
}0 j9 v+ I, F0 {; C2 H7 @# s$ S2 i8 h7 i+ a& \
) a# x4 N6 M Q1 Z3 y' |: Vvoid handle_connections_sockets()
2 t2 y: l4 e8 w) Q- G: P# G{5 z) [, l% `4 V0 f
//监听连接# A( E$ a- a9 a( O: G
new_sock= mysql_socket_accept(key_socket_client_connection, sock,/ D8 K5 [( q8 z9 X8 C) C/ g# d
(struct sockaddr *)(&cAddr), &length);( d0 u: g1 x1 P0 v% {$ v
! K2 R/ I9 w0 s1 \ if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
' Z! H' B/ k0 w: h thd->security_ctx->set_host((char*) my_localhost);$ X6 d! }3 e( e' H0 K
3 }( x: R. C. P' { //创建连接
$ v4 e/ s, U5 l create_new_thread(thd);" n; J& i8 U7 t0 s" W3 E
}) w% a: O3 q- Y) `2 g$ F1 R! m
, M% R% T1 O/ }3 U+ C
//创建新线程处理处理用户连接
. U) r8 L- r/ M' Zstatic void create_new_thread(THD *thd){
8 ~% D, ]$ U6 L+ P5 v
; l( ?4 o6 V+ `; K. F! u thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
" ~) r1 ?8 \ u% b" O0 I$ \; p0 O; r2 l8 D @5 q- g6 {' H
//线程进了线程调度器6 w6 k; x5 m6 Q: c( m
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
- W! W, y% }+ C$ a}' H' d- h% c; J
" b5 I" T5 t6 _+ i. ?1 D1 X, b% f, R) r
! ?& X: E3 V% W) I至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
6 e4 I, S; M5 N. G7 b
7 o! c/ M5 X3 M4 }2 a' `* j- E0 O: l2 S% Z+ u2 y( ~" ?* u! @ w/ {
2. 理解mysql是如何处理sql请求
- {- m. Q. |, Z3 g k. u9 C& X: I这里我以Insert操作为例稍微解剖下处理流程:( d7 q2 [ ~: r, u0 M. a
7 q+ d$ x/ P. {当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。" _& b( i& Y9 c3 v; `
, z" v2 `4 E7 ]0 B' M( i
# I' \! D3 \8 B, U3 d% u2 T5 Hstatic scheduler_functions one_thread_per_connection_scheduler_functions=2 c* k8 [/ g/ y2 L1 W
{
* A8 X" ]4 \9 Q' | X1 x 0, // max_threads
4 h5 j) p% c6 i3 F; \ NULL, // init
1 r5 k c& G/ _. f$ ^& R; W init_new_connection_handler_thread, // init_new_connection_thread( h6 o3 L8 C4 | N q
create_thread_to_handle_connection, // add_connection/ _ c& Z# z! A
NULL, // thd_wait_begin
3 B2 k. J+ z5 ]0 X NULL, // thd_wait_end' R3 v3 J5 g0 B+ l! Q! G+ y: I. X
NULL, // post_kill_notification
- ]( c0 i5 l( S. V. W2 D9 Q one_thread_per_connection_end, // end_thread/ i" p' v9 \& H3 p5 Q
NULL, // end6 T8 @! X7 J1 \+ f% Z4 a1 J/ m. U
};) b1 O0 w6 `0 E5 @
5 e% g+ O. h4 M/ S# z* A. G4 o3 C
& |$ Z" |; y, v) p从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。& ]6 l+ d0 ?* r1 O) C
" g; Y* u9 l& Q& S, y<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪( x& M- s$ ~3 L2 @ p5 {; e S7 c
' v! ~; H, b0 y& @0 {# s* Z
void create_thread_to_handle_connection(THD *thd)) p1 D! f+ \5 |, z2 m" i7 U
{0 t) l5 E' a8 w. l
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,$ m1 N0 L1 ^ @/ C/ T$ `$ j
handle_one_connection,(void*) thd))){}2 q* ]1 V. a* |# Z
}
5 k8 S& d% S1 @; i5 A//触发回调函数 handle_one_connection
2 M. f6 j# M9 a# ?* S8 C' vpthread_handler_t handle_one_connection(void *arg)7 @- W- ?% l# i, ~; ?, x7 k
{# S9 {2 L. F. v$ J/ `' n# J$ u8 V2 v
do_handle_one_connection(thd);$ h$ @6 P* U4 ]4 H" D4 Q( x8 o
}
8 L2 {, C* j! a' U4 F//继续处理& B5 O5 @7 \* F8 R, r
void do_handle_one_connection(THD *thd_arg){
1 [3 j( @5 d8 Q while (thd_is_connection_alive(thd))
) }( Q0 |" B+ A {
, g! H! j( [# i: R6 b6 Q( E mysql_audit_release(thd);
7 M6 p' u# q) L) w if (do_command(thd)) break; //这里的 do_command 继续处理- a* d/ u S8 j8 [
}
, A2 t' \ M% w6 N* E}0 d) D) Q9 D2 D; f2 U1 C# T
//继续分发
+ ~4 N9 U5 A. A( M$ f' T% Q8 A3 D! dbool do_command(THD *thd)
, p2 N& S' j% d2 F! ?6 R{8 m, @1 Z. A2 S: I5 ^$ p: z6 }/ k, \
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));, T/ P: C1 ]* }& @! `
}
/ q1 [2 k! p; c Cbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)* i+ i) ?: d v1 H+ \. a( A
{7 `; M; @0 o6 E& H' p9 Y; z9 A' ~8 O
switch (command) {
4 |" n z3 @! F) O6 W* S. O2 e case COM_INIT_DB: .... break;3 L. g( Z9 y( h& g5 ^2 ^' y
...
: G5 {. T& h' J& ^' \ case COM_QUERY: //查询语句: insert xxxx3 ?( M* e! v& }1 ]9 n4 X2 \
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
8 X! `3 @/ u" E- ^$ ?' x& f, G break;
4 X7 g$ N z6 W$ J* b }
8 G' f1 K2 v) E- ]; y}
3 g, Y" l# N2 i. K# \//sql解析模块
6 U: Q @: q [9 X, P0 }; Vvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
/ M5 p, A' P( O) M: U+ B{
+ P- T( b w+ u8 f8 c error= mysql_execute_command(thd);
! G9 U( R) }# N1 @" k' ~}
5 w1 t- ^% R C* F* R6 F- s0 f% W7 V6 s1 j
6 ~! D& l, N2 o1 ~: ]+ K
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。) b4 A: A6 a8 E0 M0 L
/ c" @9 {; G( E1 B+ Q
//继续执行$ a% ?( v) F' h- x# w% k: ^4 h" N
int mysql_execute_command(THD *thd)" R$ ~0 e% e" [$ X9 t' J
{
! Q* a5 Z6 ?- o) U& |; E switch (lex->sql_command)
' Y: I/ C" D! k9 J5 _3 I {
* Z% a+ _9 }, S: |! n+ R9 O case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break; u8 Q- g; |" o, u' t7 B
! c, L9 p9 s7 n1 K: `* t //这个 insert 就是我要追的
& t7 M" {$ r1 I6 f0 ]( W1 M4 ~ case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,; s: b/ V$ r& }& {7 g& ?1 f5 Z
lex->update_list, lex->value_list,' u& B% h( y! ?+ q
lex->duplicates, lex->ignore);
7 ~6 Q$ F' y8 ], W" [( f }& }3 D& v" N, Q. @3 p" U0 N
}8 f. j/ C2 Z& g9 y
//insert插入操作处理) e L- ~* \ V: @9 k5 n/ a; b, p
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,& u& _, e/ _. z
List<Item> &update_fields, List<Item> &update_values,
. v$ C/ P: g' U" P# \( O! P enum_duplicates duplic, bool ignore)
- l; _) e% p) B; S2 y F{5 Q% L) T4 V7 _5 I& u2 v1 l3 S8 c
while ((values= its++))6 _' [5 }0 P" u* K, b
{2 O8 p" q7 N+ @5 Y+ @ ^0 b
error= write_record(thd, table, &info, &update);5 F5 s; x* B2 k2 ^3 H4 J1 L
}
- W4 _ H% D- ~0 d! W5 Q}
" \$ L6 d0 q3 \' W//写入记录/ K6 E1 W# P$ L. R
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
2 e, O0 f* Z# y+ r! r% H! G{$ a3 r' R4 B3 V( k3 {) k2 F
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE), x4 D7 |3 I5 i) z0 `# L
{
2 ]6 [! _; Y# h$ ~" Q* t& J // ha_write_row 重点是这个函数* @% t8 n& q* ]
while ((error=table->file->ha_write_row(table->record[0])))
; ] J: U5 S" T/ H {3 V. W" T) x8 q$ \$ [, O
....
+ C3 c7 K* U. g! q# `9 {: I: ]. Y }
, V9 f5 v r7 ?9 Y; E }$ \5 U. D7 L/ B6 U2 f9 B! K% ~
}
* }' ~' G* L& }& s- A6 h; q1 b$ ]5 a6 {; p( ~3 o9 K
# s2 j4 |+ t9 c* h" f3 n7 o9 t; |$ _0 _, m. B. O' ]& `# b B0 _
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
. @& r5 U8 O7 O: e ]; M; M6 H: x4 E- Q- r
<3> 继续挖 ha_write_row
& O9 T' k5 ]1 I8 u( z/ E4 W, L8 m0 s% `
int handler::ha_write_row(uchar *buf)7 ? p; ?+ ~7 J* d
{/ A2 d+ I+ i$ v
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })9 _ o+ n O) u
}
2 G( e+ e' s$ [0 p" z7 F
7 n% ~! y3 j- l" P0 K. S//这是一个虚方法
$ Y+ H7 z- A5 v# [virtual int write_row(uchar *buf __attribute__((unused)))+ o9 {, j# \6 K3 J5 x, c
{
; E& i" `. e* e return HA_ERR_WRONG_COMMAND;
- a! K- B- |& ^4 V% c}! J I0 L/ g- X
% i3 ^0 {, U4 B6 J% f* F" ]9 q( o3 G; G' e! g2 S
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
, n8 j& _4 w6 }( _. B: I2 C- g6 Y. U' \! ]
3. 调用链图# s/ ~+ I: ]* g4 z+ |5 ?7 G
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。) t" ]2 @# [, h0 n+ ]- P' E
, _" b3 U$ O/ G( p) j6 ^* L
% B. {9 w: M3 G( ]: C, a, f
3 y, h( K4 }+ }4 u6 g! T( j
三:总结
6 `3 L2 g! z, W/ L4 q, n大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。. t$ g# K2 P6 k1 @5 m6 }; b: J
————————————————
2 T) Y. j0 g) G( \* _+ B$ i版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。6 p F9 ?4 \! M# i+ L
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
2 E2 e% [0 P( q8 ? |
zan
|