- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55557 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17619
- 相册
- 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 a: o2 W' m+ J8 T6 g1. 讲故事. w w5 d& m( E7 l6 Y
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
( ]1 l* Z7 E/ D
, E5 a' A5 c) l; b! ]二:了解架构图
4 }9 M4 P- e" K0 l3 M' E( imysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。" n/ ^& X; k8 a1 t3 h. u. j5 D5 a
/ v ~. x/ J7 p# u' o1. 从架构图入手8 K! s2 G" c' ~# I$ ^9 z: B: d
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
% d# U; C n H8 D) u![]()
8 T) S4 |' {2 I% J1 Q2 u* r0 o. ]
8 r2 Q- {+ ~6 X& q/ J5 O
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
5 D6 A2 c+ E! Y6 Y6 Y& g" p8 ~2 N2 s
2. 功能点介绍
2 o6 l8 Z2 ~! B: S8 bMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。% I3 U% U Y& x7 j
: z8 A4 w. `* ?: a2 Q; ?" D1 l
<1> Client. f+ Q+ q0 i2 D( d) f0 g- ^
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
: {; R& j" Q. Q' R
P9 n, \1 X E+ ~. t5 r/ N<2> Connection/Thread Pool; b% K( M5 z/ z- d- j
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。5 N' U) g: t% ?
& [3 Y5 x7 h" f<3> SqlInterface,Parse,Optimizer,Cache
" X% j& N2 n4 R8 ~ Q# G" m对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。( M- [1 n2 C: w) a8 n; i1 T4 n {5 R* Q) j
) A7 h/ B1 J: |4 K0 k' \( B<4> Storage Engines
0 w, g4 @0 M+ R& U0 F& R/ x5 F: E负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
- s7 k' S! H- q+ [' R1 a0 v6 o& e8 w5 r' M" W" X
三: 源码分析; \3 i' y$ F6 ~" e9 t2 |6 j( A
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。5 H% k: f% x* }! a8 ?) T
2 s: `4 {+ p1 S, B/ V, C j
1. 了解mysql是如何启动监听的. t7 ^ ~( p( j$ A& m
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
( i6 t/ D" d, g! n& x* F + J; x$ |* g* `( o2 z. _! b7 J
0 g; c4 ?1 {- o8 ~% m. y
% f' v0 A4 k4 I1 [, o% Q/ p! N从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。/ ?7 u) _6 W5 j
0 n# u7 v8 k0 m6 h2 {; v1 L' P<1> mysqld_main 入口函数 => sql/main.cc
# q4 Q7 n4 ?+ r' u9 b U( e/ P1 w! K" ~% m4 n
( c. h! C& ?. m! |# y4 w
extern int mysqld_main(int argc, char **argv);% m6 O& J A2 b5 m F& X6 z" r
5 ` \ a4 i1 L0 c; A/ ?3 uint main(int argc, char **argv)
r5 k! J! ]/ K{6 Z8 S8 [) U( ]5 Z) ?' I0 J
return mysqld_main(argc, argv);
, _7 l: W; h0 H3 g- c& |& _}6 n! U* b: }, j' f/ V, M4 n& @
! A+ O; k. z9 a4 z2 g" G2 L& M% G& Z# f
, t( Y E9 i* a4 c( Z3 B r
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。$ n$ m/ [: D* b0 V E0 B4 g
( x. A$ o k- u<2> 创建监听4 \* l& l6 o. C! _1 ]" X
: y! t o5 o8 _, q3 m! T+ ~
' H# O; j) B9 c! w" A! h" f- i6 @/ zint mysqld_main(int argc, char **argv)1 b c/ l4 w9 l* e9 `& o
{
: n$ I* h' C; K+ ]: l7 F //创建服务监听线程4 M6 _2 m/ d4 ?
handle_connections_sockets();
1 I6 ]5 [7 W6 E}% p/ ]: s. c5 i' E+ n r# }8 m
$ G$ [# ^; s: R7 y( ?void handle_connections_sockets()
+ ^% Q1 P0 r6 q/ @/ \3 H{7 ?5 n G- p7 {+ Z
//监听连接8 \! p3 U v1 h* T! e; @
new_sock= mysql_socket_accept(key_socket_client_connection, sock,) k( B5 K4 f! v
(struct sockaddr *)(&cAddr), &length);
9 G+ B6 J8 V+ |4 y/ v/ F* D$ J2 E( y" J: N1 x
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
# Z/ `+ }& g6 w& h thd->security_ctx->set_host((char*) my_localhost);2 J+ g# a! Q' x3 V/ ?" G
c( F: q: N2 Y/ [- j0 c. g
//创建连接
% j0 }) b F/ H1 G create_new_thread(thd);
. D* t) {. K& l0 o- W4 \% S, W}
9 ~: V5 f' F: U' J8 w5 s" U% ]! E% ^
//创建新线程处理处理用户连接0 H+ H3 Q' F/ ~6 c1 F/ X4 x
static void create_new_thread(THD *thd){6 H* j5 z$ n. |. b2 f7 n
0 R1 y! X, F% ^- u3 i, v: V( O3 \
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ c+ e$ \+ U1 ^- H3 X" ?" Q/ U
; P3 C! K/ ]* ]8 ~# ]% }8 y7 k //线程进了线程调度器6 @& F) v0 B- f. c! ]) K* P' _
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
- O& J; _( R! q8 m}, K8 k4 L6 f( Q- U* S( g
; ?' q4 k: ? \4 {# S; [
. A1 P: n2 t! V6 X: s至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
/ L, h. x D* B5 R# I5 I" |
1 ~6 [& M g- ^% G
1 D+ |) y/ p% |# r2. 理解mysql是如何处理sql请求
+ f$ r5 l+ b9 ], v这里我以Insert操作为例稍微解剖下处理流程:
4 i3 a' @ p9 Z. i6 R6 p! M0 K! ^
6 ^8 j6 X4 ]1 L0 n) z; s# x( z/ h当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。; K2 m7 I* v! q9 B9 `* o; \/ D
5 U5 [9 _9 Y1 q9 Q- m1 R' w: C6 s1 T, g1 M# p8 G
static scheduler_functions one_thread_per_connection_scheduler_functions=6 n' r0 {. [! M; ^; F
{
- p" ] e1 C4 ?" o7 f 0, // max_threads
) {% C6 v, B! o) C* F# H9 ]2 u" y NULL, // init$ f) P7 S/ z9 K0 v
init_new_connection_handler_thread, // init_new_connection_thread
3 v# Y \( y3 @1 ^- w3 z create_thread_to_handle_connection, // add_connection, [6 `: b c# n) S
NULL, // thd_wait_begin$ O+ q8 n& W3 y
NULL, // thd_wait_end
j( p8 b* E4 B; S1 A NULL, // post_kill_notification
' K1 p. L) |) ~ one_thread_per_connection_end, // end_thread. f! X7 t3 T! |+ t2 C
NULL, // end
m! F5 _ R1 O# R, G: }6 R}; e% ?! u" V8 q4 T- }" v5 v
, h" a/ ]/ l- n6 J9 ?6 ]* ^# m
1 F6 N$ ^' N4 D/ j" |" w从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
' v( B5 O1 E. i& `/ K1 G+ G
( V9 |, }- a0 O; Q# O; Y* S3 E B<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪5 L0 E& f1 Z/ ~8 ^- i
, Q5 d. W) i% |; u' K
void create_thread_to_handle_connection(THD *thd)# s7 i4 u( a" @# l: Q5 K
{* A8 n6 @# ~. Q, y5 Y% D9 d& c, b! N
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
0 T1 E/ }) Y Z# I) B handle_one_connection,(void*) thd))){}
! ^5 V+ t$ ]9 \( A, m- {8 d* a) K}
, \6 j% q# J8 D//触发回调函数 handle_one_connection8 ~8 B2 Y8 f- D
pthread_handler_t handle_one_connection(void *arg) M% |, z3 S' ]
{
9 {" V2 ]1 b% ~. H3 \ o# W7 ] do_handle_one_connection(thd);% n( t( _% \, [- C
}3 t( [+ p `$ I$ p0 X( `
//继续处理
7 N6 p5 R) o" Q9 ~; P" Zvoid do_handle_one_connection(THD *thd_arg){7 ` X/ d$ v, I1 x- R" ^! ]& n
while (thd_is_connection_alive(thd))/ i% |9 A# j. @' M: h0 k0 j( a
{
/ d; Z2 R+ V8 |& B mysql_audit_release(thd);8 [2 \; d. v: r% z B* F
if (do_command(thd)) break; //这里的 do_command 继续处理0 Q7 i: g# l) @, @( O2 m, G" @
}
! X+ e" ?3 a6 v0 i( Z& v( D}
2 b- k+ q. Z. a0 b//继续分发
7 a( w1 `' f) n6 @% B8 L8 abool do_command(THD *thd)5 o7 F- l3 F# k
{" A d- X! }& Y1 C; i# f9 }
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));) a- @' e4 g; z( U
}
q! ^* r, h( p( ^1 O$ J1 bbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
1 \/ J [+ h+ e5 m0 M{
, E) o1 M% \3 g- q! Q9 N switch (command) {/ r! K. `" s& K8 p" Q) x
case COM_INIT_DB: .... break;
8 k; V; I; U0 k ...% g9 Q' V) [' @
case COM_QUERY: //查询语句: insert xxxx5 n1 E, V9 V1 g- n: t' ^& S
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
5 J1 ?' [% o) e6 A break;
& `5 v$ E5 O" G% ~ }
3 f" @# V- N) m4 S5 |0 }}
! U& W4 C7 ~6 _//sql解析模块$ ], v" Q; E1 K6 y( @* f
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state). k$ _4 a# i$ J# U7 G
{1 G, F5 ?1 H2 V2 Y$ Q9 ?" Y+ j
error= mysql_execute_command(thd);
7 K; ^: T: {: h+ G}6 @# |( b+ p1 g+ q& u. v; {: X
4 n$ V# I8 i( c1 K
9 R; H+ \' }" Y/ q' b+ x<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
, Y x6 n N# V! q* A' {# M6 ?) e& H" b5 r3 o; O: A
//继续执行% D/ u. }% p" ?8 x
int mysql_execute_command(THD *thd)
3 |& R) D5 U" r- L9 h- T{
" x1 m+ f) l: R- G$ B switch (lex->sql_command)
: q' |9 N% j; @4 Z* j O$ R% ]* j {3 b* H1 p S! J
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;7 r; a: ^5 x; @- t" e& T6 X
/ [3 H5 Q) @9 I! B
//这个 insert 就是我要追的
+ a$ B; J$ F7 u" H& H% r. _ case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
P; p9 k" ~9 l lex->update_list, lex->value_list,
" }8 x3 q3 A" Z( B9 V; y lex->duplicates, lex->ignore);8 Z4 q& W( ^1 W5 v$ G, [
}4 t6 m$ z& S# k# E1 S0 w
}% D6 ~. o0 M3 ^
//insert插入操作处理
/ V7 ?( _+ w! @bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
. r# c7 @$ ~2 |: D" I6 S List<Item> &update_fields, List<Item> &update_values, # ` L; j1 a: t
enum_duplicates duplic, bool ignore)3 n# ]3 n% y6 _) E
{
5 G4 i# H" a) ^1 Z# x while ((values= its++))
8 Y) s, U4 @* a* P( ]" D {
' ?5 c. }* b$ W% L7 Z W2 r& k error= write_record(thd, table, &info, &update);
; A- h. y+ s$ s1 v$ T( V! b }
9 F7 U* t4 j, Q4 X) M3 r}
8 o5 L, `7 w. {- g7 q# ?1 {//写入记录0 t: t* j' F+ Y' _( N
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)2 t* j# x2 t2 U+ X4 t; I1 k
{
' R8 x- k4 r( V0 Y3 X/ x. y if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
" W8 L, y) K- N {7 \- s+ o/ S* N: `0 ~: Q
// ha_write_row 重点是这个函数
. _3 e+ H* q4 e9 P3 {& O while ((error=table->file->ha_write_row(table->record[0])))
+ H2 j6 Y9 R r' n- y; d7 @ {% K% N) R1 D3 P. G
..../ G" |) b+ ~3 s
}
0 W9 M; J+ l2 a6 C/ m }, \ i. E& F; d5 o3 H5 T+ B. H8 k
}
. d" ^8 |6 q5 i y/ f' L1 L) W T$ w P7 C) P. k8 @, [
7 d% G: e. a! x' S/ _
( |6 ]$ n N) |可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。2 B1 x3 `" a4 Z" Y6 h5 U0 J! j7 L
9 }4 W* ~6 r$ \9 r2 l
<3> 继续挖 ha_write_row
7 f: F% \) f+ y1 L
% S4 J q' ~$ kint handler::ha_write_row(uchar *buf)
7 N/ d6 b- I8 n B: s{+ w+ d9 E3 v% |) `# R, M
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })! N( C: l& ?3 _5 S
}+ Y0 B2 t, N7 X' Z8 D# Z
2 y% X" U3 H4 l! ^! q
//这是一个虚方法
+ R3 N0 A; d) i1 b; Y3 }9 A5 i: C3 Tvirtual int write_row(uchar *buf __attribute__((unused)))" k* r' Z0 C; w! k
{
& A' }/ W, {1 p. U# [3 T return HA_ERR_WRONG_COMMAND;
0 ~7 G- c, W" W4 Y}" l# M. p/ f" m
g' |. [% U( [/ p0 t/ o/ ?
$ d; D+ {% S2 a \# v J# Q. L看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
" U9 o" q. Z# } c; C0 E3 _, n+ }+ f2 Y2 T* K( V7 b. m+ X+ F
3. 调用链图
9 A* F" J% b* } N/ k# B这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。; ]- b( T$ o) R5 \
$ x& Q5 W! o& O% ^1 l% Y+ z' A/ l
/ c. r; u5 t+ ?8 y6 T9 U0 r- v
: H# W" F9 ~' c! B3 H三:总结
* @" q4 V) l5 j3 \- x( o( W大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。 R. t4 k2 w% k8 E1 x* }
————————————————' u$ D! Z6 r! A S0 w `5 Q) Y+ x( n
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
5 S7 |& G( j* S$ L% \原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
" T; |! |7 ^5 \8 \/ c. m! d) D4 n |
zan
|