- 在线时间
- 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 田老师国赛冲刺课 |
一:背景
9 U* G, x; E8 g# J. [1. 讲故事
) @- Q4 n4 a3 ? a最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
' m% d9 n+ {! c+ L+ B6 K0 I* b! x3 I
二:了解架构图7 s, q) l+ U4 S4 ]6 n
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。3 W, e& z+ i, z9 P9 @
. r, \' G4 o/ T4 N3 W! z1. 从架构图入手 D, ], v7 ~- Z( E0 P8 s( J) |
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
0 d3 e3 u( T$ g) W3 k, e+ y![]()
$ d5 i8 K% ?8 T' U" F3 D5 ?4 N9 u& g
5 z' I8 o. @; a( S9 _/ e0 j6 M
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~* f7 m4 M3 }# C* H% j
" ?: F+ L3 l7 h1 w' h% M1 @: d2. 功能点介绍
: ]% m: V! e! r2 s$ QMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
$ N8 D, k' U$ d$ a& u ^, `) r) q0 f9 O4 ~; G; S
<1> Client
! m$ l% u9 P' p不同语言的sdk遵守mysql协议就可以与mysqld进行互通。$ O4 L% s2 e& {0 U/ w
& N8 j- r/ V- H
<2> Connection/Thread Pool4 i! S9 W# o/ S3 V! J$ A8 p" U% H2 T
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。9 C" b. t0 ]/ j; r7 c0 V
+ J5 i( L8 M1 X8 o+ n<3> SqlInterface,Parse,Optimizer,Cache O* v* @5 w" z6 x5 c& d* V+ D' U! F
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。2 Y, e, t& N) k- c! j& o1 B( F
3 L% v. K) `5 d3 N" ]7 L- J) F
<4> Storage Engines8 F3 ^/ q3 p- V& m3 W1 A
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
0 Y$ d2 p5 B+ |' H; J7 I6 c# h3 j' H8 c, S9 D
三: 源码分析( W" T9 ]7 `! g+ S) d: P6 T
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
3 K( A, [, q" K1 Y- X
8 M$ z+ G' b) |8 R1. 了解mysql是如何启动监听的2 m- k$ w( E1 |8 v/ `' w
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。! W7 R; [) |! d U
![]()
' S6 P, ]4 A- M. O. h5 ^' g |7 _( W2 U& v" j; F8 L% [
3 F; U' w( F& y
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。! W( [: o+ b/ o3 a J$ v1 S$ D
0 c; N( ~) n# F A3 l' z! e$ o
<1> mysqld_main 入口函数 => sql/main.cc! {* C0 M6 A. e: d5 l' @
# l8 D$ ]9 E2 t# x
- D: ~) O. F$ o4 T; J& f1 kextern int mysqld_main(int argc, char **argv);
1 O% V; }& B( D* p6 i( `7 D5 M: p1 V- a( z/ O
int main(int argc, char **argv)4 I. B" f; C. c* R% V* M
{% p! Q' b9 R9 R p7 W: t, y
return mysqld_main(argc, argv);
/ [- ~: S; Y2 Q3 K}7 |+ J+ S! _% X
. V' J) R3 B ^. t2 w0 k
. N) ~: f7 c0 t2 a" N% Z这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。2 _/ E- f2 o! G- W2 n* z
( E0 C* Q' s% S" R2 @& F" c$ }
<2> 创建监听5 D8 w3 {: ^0 w. I
. \. `& H' p4 |+ K1 Q
, ~6 z: x/ m3 p2 uint mysqld_main(int argc, char **argv)
. h6 R4 P4 Y5 T# S' ~{
8 a( B. J5 M6 @! ^ //创建服务监听线程9 T1 |2 i2 U6 r+ d; H4 {
handle_connections_sockets();
& {( A8 Y0 D' W- X. G$ n8 c}/ J" d! z9 l; w0 C/ ~' _) O
9 N. a2 ?" i* e" x( _6 h7 [" _
void handle_connections_sockets()- _5 S% ?& g4 |7 a& I
{( v' c b- Q! F& Y+ V8 t% p
//监听连接
2 j. }" s# s7 `! ~, o new_sock= mysql_socket_accept(key_socket_client_connection, sock,% {6 a- I/ x+ N6 W: u( u3 i' c
(struct sockaddr *)(&cAddr), &length);
* i' ^- I% f: ?& R1 }; A1 N$ y; i- o, P, ~. `- J$ [6 \ D
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
: H( z9 }- Y) t! M thd->security_ctx->set_host((char*) my_localhost);1 n z; D; F. n2 F: D! Z* E& h
9 f- m/ q* M: C) ]( Q' H
//创建连接
1 w" h' G$ I& @$ z& r create_new_thread(thd);
' z3 V% z% L: w}, W, O. e+ _" U5 n4 Y& U7 W& X
1 G3 f( @0 z3 M+ o
//创建新线程处理处理用户连接( o2 [; _& Y. T
static void create_new_thread(THD *thd){
% g( v& P" G1 s3 ^' {, J/ x5 @4 i
9 ~' O4 |5 i$ |0 z5 m5 E thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
# _! }9 z/ f& `9 o5 p! {. Y3 c6 F( J0 x% \0 ~3 {; T
//线程进了线程调度器( |2 p& m L0 B9 ~( h! X: y M* O
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
4 |! y8 D: }& m6 p6 p2 m6 D}
5 H' [; [! r* j F: q) p
3 F h6 O! [7 n. {2 o
. o# Y4 R& X) @% @1 Q! J% _2 m1 B至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。7 X& ~: Z' o. B
+ c; j' F. M* ?/ ~9 q/ D9 Z# v7 q7 \" |# R
2. 理解mysql是如何处理sql请求
# b: W; u J. b这里我以Insert操作为例稍微解剖下处理流程:
, a6 F8 ^9 I# ]) j, V+ ^
1 ]' M" ^5 J% R$ N当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
M- t5 x. Y- T; F) m3 B; }$ v1 h4 D! i5 ]; } X) R9 S; c
5 T+ m; {- ~( J# n7 |( p" h: f5 Qstatic scheduler_functions one_thread_per_connection_scheduler_functions=& Z# O" E4 V" D2 ?; j
{8 K4 `2 ^0 w, ?2 [ `; I, d9 U( w
0, // max_threads G4 a4 b, D( U$ D$ Q! L
NULL, // init$ R5 K/ j; N" I P! e0 N% e$ B
init_new_connection_handler_thread, // init_new_connection_thread
a9 G# Y- ]0 i* F4 m create_thread_to_handle_connection, // add_connection
! k. i0 }* }) }1 C8 {0 h f1 q NULL, // thd_wait_begin3 n8 O; g" I5 Y8 h9 N& a1 |+ p# G) u
NULL, // thd_wait_end
* B- ^* N" b) d* {" Z) B NULL, // post_kill_notification3 B+ A4 w7 e1 p4 V6 a% m) r
one_thread_per_connection_end, // end_thread
1 J8 R$ J3 ]7 _/ n: s7 p0 Y NULL, // end
' k: h& A1 R- R0 X( z};
& Q( m. Z; F; H' V& X; b* Z% j/ N" |: u4 q* M& I0 n+ P
. R( ]7 r* v! {6 e0 N从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
3 g" D" Z9 U$ q5 k" R, I; K6 G. `; R
, v- t. Z+ w# _* N<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
8 B5 b- d1 K& z! F2 T/ b8 t! @3 H, p, R6 h0 _8 y
void create_thread_to_handle_connection(THD *thd)% N/ m9 L$ q# m- c+ n: t
{" Q) [0 r6 \% O
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
; i ~6 n/ r$ C1 Q handle_one_connection,(void*) thd))){}! l1 R7 a7 G' P
}( u; V( n- l& j6 I( ?
//触发回调函数 handle_one_connection
' p; c4 E2 u8 i- }2 ]: g: H6 c X/ ?pthread_handler_t handle_one_connection(void *arg)6 F2 r2 f' Z; T% {5 H: K7 ~
{
$ Y0 G: p% T4 ]. v( e2 { do_handle_one_connection(thd);
* Y3 h8 Q7 N. j1 Q" O* f! d}" L. R1 _2 z. W' B! c! n4 Y. Z
//继续处理
: l& K2 {5 ~9 yvoid do_handle_one_connection(THD *thd_arg){+ E, t4 V/ ^- ], I6 t' V
while (thd_is_connection_alive(thd))1 ~( E' h8 I3 S+ z
{
, K2 _% N1 o8 x4 b' |* c0 P/ c+ D# R mysql_audit_release(thd);
2 i* d; w$ a% R4 T7 X: n if (do_command(thd)) break; //这里的 do_command 继续处理/ C% d* y7 s6 N/ X4 U2 J& |3 O
}
9 R4 C7 |- g( v9 w, M}" x3 e e7 [9 z1 T5 Y# Z
//继续分发$ ^. T3 M' i5 ?/ `
bool do_command(THD *thd)
) T- U K6 c+ e @! U- a{8 O, |4 d' E9 a. ^0 m, [- ^
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
5 l" B6 O; K3 n; V/ a4 ^}% A5 A- @4 J% y- H" z( R
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)2 f( i* y* x5 { V* {, p
{
2 s9 J& C8 k! s& t) j: P6 N% i switch (command) {/ {! g, B6 A3 Q- U0 i& @
case COM_INIT_DB: .... break;
9 ^/ B8 G% _6 {+ @ p& M ...
8 z, }/ p( G+ f0 m& s# o case COM_QUERY: //查询语句: insert xxxx
2 B0 A+ L' U0 g- D7 o mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析4 W/ u2 J( [9 R
break;
! x' b0 h# p# w; w' z }8 z3 B6 o- d: t0 \5 h2 u+ C2 G# ?
}7 k9 V+ M, z. } m: A
//sql解析模块
; `& i, Y" w/ o5 c/ W3 J# vvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
, c' @0 t: R0 \2 ^8 U{
2 y- X* ~2 B6 e! I! a; w error= mysql_execute_command(thd);
8 `! K, v" ^+ m0 m3 q3 f" } d}
3 t) M9 V, }3 F6 w' w" w1 ?/ |9 L" n: \& y) y8 _% c# Q
8 b* @9 W2 q* x; M" ?+ s<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
2 ^5 Q. r+ B9 G' B2 E, @7 a! Z: L* u2 Q
//继续执行
7 K8 c% k/ k7 c3 n3 |$ vint mysql_execute_command(THD *thd)% T! _: n9 Z8 c% u
{& j9 L9 g4 D7 S/ N
switch (lex->sql_command) & M1 G2 A i2 @5 g0 j
{) V" [) h) o( G1 X7 L( h' Y
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;0 B/ Z& p. w5 J4 h1 o- v/ x
# J/ _: e7 Z6 Q1 x4 ^
//这个 insert 就是我要追的
+ S; x U% g K/ v case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
$ w' R4 c& H& Q! C+ F- L lex->update_list, lex->value_list,
# P& J3 o: a( f& U% v lex->duplicates, lex->ignore);) Q. e. B3 F/ U1 w+ T- `
}( X& ?( a0 I6 V/ q' i G5 M: [. U
}
0 \" V" x7 W# \& ~/ r//insert插入操作处理
: g2 u8 l% m$ | L) k" j9 [6 ]bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
?0 d0 x3 u" L/ b; a List<Item> &update_fields, List<Item> &update_values,
1 ~; F. [8 C: a, B0 e3 | enum_duplicates duplic, bool ignore)
8 ]1 b: T0 F; }{
! ^- H3 y1 ~# D% d) |) h while ((values= its++))9 f5 I4 t: \; m' A
{
9 }& N/ X$ R" D% [& s error= write_record(thd, table, &info, &update);5 _6 [& J' `% M: Z
}" e# i3 u t- y8 w7 r
}
" h6 k( |, S8 n; ~6 S* V//写入记录
+ y/ K2 |, N* @# B5 s& iint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
" R. C0 D& C5 k5 Q& V6 ~{
8 d1 P; y( f4 H$ L' P: U if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE); z O/ S P! h* S
{/ G. u- w& i# L7 E+ }2 N
// ha_write_row 重点是这个函数
6 l& `! G$ v2 L. b1 U8 T while ((error=table->file->ha_write_row(table->record[0])))9 r9 C- R" j3 i! Q" h9 R1 T
{
( O1 C# p/ T( M W e7 z { ....' Y% p5 S% {2 D( @
}
- I% Y+ m5 H4 |1 \" S }
% S4 Y3 ?4 F0 O' u" u}
; I- `2 ]$ ` d0 l' q" x) C5 m/ m2 v0 U1 ~+ Q( z6 O. U3 K
. y* t2 X/ [( p* v4 F8 c0 t- a5 s) b* A( d* h8 c4 M
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
- N; T* E0 P( W" b5 N% H- c
4 h; ?1 v$ y, N2 n T( O q( V<3> 继续挖 ha_write_row- J7 o8 |$ ?8 X# b1 ^
: O% Y% v7 o# N* rint handler::ha_write_row(uchar *buf)
( b( Z4 p: p5 o5 _0 R7 P$ @- W{; l2 a; H: c R
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })3 Z! P- C! U# q/ P* Q
} C2 I7 c4 w& Z' [, w& g, [
$ t" U4 Z% U- t* d, p
//这是一个虚方法+ q' h3 j5 k0 v, i5 t
virtual int write_row(uchar *buf __attribute__((unused)))- ^! B; l7 K" {1 @* a
{
: T7 X, i6 k8 E4 K, V return HA_ERR_WRONG_COMMAND;" u0 [5 A5 A; G7 W
}+ {+ C1 F; H0 T' y, N% M5 l
8 D' g; `& F A5 d% @! }2 Q/ q# H3 H5 Z
; S# Y3 c8 |* [. ^3 a) w8 o
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁% c1 {( O9 L; f% B
$ l, k) j G4 T x1 W
3. 调用链图
7 ?; b% Y" n) i2 m, h% l这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
* V: V: u% s' D # R: O( |' J X- q
: M; @+ b% D$ C, _2 r7 v8 g3 q; K
& f3 T3 a& n5 k! h三:总结. Z ^5 F5 B- ?# A$ R1 ]
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
4 F( k) X. ]6 j" z* F3 ]————————————————9 @ Y+ P! i! G; k6 N1 h0 |
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。& S9 ~: R+ T: x* Z0 c, m- k8 s2 {
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415# b7 F- O7 a: O" w+ A# e
|
zan
|