- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55556 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17618
- 相册
- 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 田老师国赛冲刺课 |
一:背景
- N- j& _- ~2 z4 u* p1. 讲故事1 }: d7 r$ A% g* b; j1 H
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。* L D8 {2 E: G" {# v
8 L3 Q# w3 ]& R
二:了解架构图; h I: t. m6 j
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。6 d- c: a+ w8 l% Y
$ d! N. u8 w/ N7 S8 p) G" |5 d2 ~
1. 从架构图入手8 o$ w% U# _- k
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
- B( U# N! g' {; J' ?![]()
) r; p u/ h( A- u3 G" _% `( c, F- u
) r, C/ X8 O* Q- R7 Y) {1 g" h其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~* z; Q" w* t9 o1 L
$ l4 I6 D1 }& B2. 功能点介绍
6 p2 r' I; A9 \MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。: W: P7 j. ^; N" r- `/ \0 H D
d& c+ C, q$ i: a& X: x. Q
<1> Client
/ n- O0 }. d6 e" m, w6 M不同语言的sdk遵守mysql协议就可以与mysqld进行互通。+ w: k- X. C6 q* x5 I9 W# g" B
, c" y. C1 Y2 m: k8 n& Z
<2> Connection/Thread Pool
( M. ]6 l( M. e% Q. j- z! a+ aMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。7 y4 ?0 s. Y2 b; u* p, K
! @0 D$ S& V3 E% l
<3> SqlInterface,Parse,Optimizer,Cache9 @0 X4 \( j ] h- F* Z" G1 {9 [% G
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
5 D+ n+ J9 S! z! M3 t R& g) k* n3 g6 @3 r
<4> Storage Engines; w9 b2 c, W+ E1 p3 D! x
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。) S5 s5 @, W/ w, L$ Z
7 |8 [. J5 E7 M+ [' `. t3 X三: 源码分析" i) [- X/ a2 V2 U
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
+ o% {/ z; ]1 Q0 o
3 I* t$ O# Q) | \% _1. 了解mysql是如何启动监听的
l: r8 M$ {7 B6 w! ]7 S手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
& _* N: t! _: K( R M' w 5 c" m Z1 }" @* Z! r7 ]
2 A# W, U0 V- L9 B% |7 I9 v3 _* j# v3 ^) y% a) j
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。% ]. U/ I/ m% I& H9 E1 @1 p' m
7 Y6 S' Q; F. Z& J ~9 g<1> mysqld_main 入口函数 => sql/main.cc
9 y8 S3 b% s* f% h0 q
- U$ I% ?& h7 v+ X
1 t' t$ I8 ^( V0 }7 gextern int mysqld_main(int argc, char **argv);
6 ^& {$ a- x& v- ]% w5 w4 K0 \3 s0 O1 ]( Z/ t/ W+ p
int main(int argc, char **argv)
2 s2 E1 O5 t3 k0 q H# }. P" i2 I{
# c% P4 i1 O+ S& n( P return mysqld_main(argc, argv);
/ \- k. {, L0 C- X9 L! w% `}% Q1 a( Q. D# p: u3 \( r" n4 V
& N) H4 a( j# f4 H
8 ?9 N: |, m) f' y7 D# [这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
/ t5 L1 p, q% ?( v. S3 }- B3 b
( P$ i# @; {4 @) o* ?<2> 创建监听3 ^% F, O3 ?0 M+ e3 w( T" b( r% X
/ g' w& _% e$ ^- \/ H2 k
3 Z/ X( i0 i: Aint mysqld_main(int argc, char **argv)
7 d. k& r: p6 K2 U6 _{
3 b. _8 |6 T' C# ` //创建服务监听线程. [: [ K- b; U
handle_connections_sockets();
% T1 P" Y; L% {}; h' p; h& d, \- g
' o- Q7 u' q2 D7 U" m
void handle_connections_sockets()" f, V) k) l( ] Y$ x
{5 }8 `2 C$ k% n5 \- g9 n9 z. q
//监听连接
; U% N$ t* u F new_sock= mysql_socket_accept(key_socket_client_connection, sock,+ |9 X$ f) H$ l' E w
(struct sockaddr *)(&cAddr), &length);
+ N7 n: \5 Q/ L" n; [% [. X3 o/ D6 u9 g% S5 u- [7 B! ~( w
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))% s6 [) r! {3 b7 ^( h+ w
thd->security_ctx->set_host((char*) my_localhost);
; h4 P$ w8 ]" E+ X' W0 V# G$ {
3 F |' B! P; c. }/ ]2 E. T //创建连接
- B7 J8 ?- M$ z1 v3 ]6 Y* u8 D' }3 f create_new_thread(thd);( v6 f! t5 H% c# [/ c: J% }4 D
}5 A9 \8 H9 ^- ~" \ W; Z4 }) Y) i
& S$ W$ h+ H/ x/ F# e
//创建新线程处理处理用户连接
% D' s! N* D% K% n" h3 ystatic void create_new_thread(THD *thd){
. f( P, m, B% C# `4 X: H4 b8 l2 R p r" U. |* l( ^
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
7 u/ h, c/ c3 ~1 K! @8 i8 i0 c9 D: j& Q2 h- x: S( N1 ]! C! Y# d# [
//线程进了线程调度器
+ S4 c- h5 [7 n* \: C) s MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
7 M! Y; Y- X b; o w}2 \5 @2 y E" ?+ u( ~5 g
3 H5 p3 i8 Z5 s K; |, S- M
7 W+ F( e8 @# |, K
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
$ N q$ O+ z% b& \
5 z" j/ A- P& z/ O' i5 ?( U7 }
- C- H j9 m! Z( N' `0 m% `" i. B2. 理解mysql是如何处理sql请求
2 X$ F% R* v* A* ?3 ]$ w" F这里我以Insert操作为例稍微解剖下处理流程:
# y" B1 w% M# L m5 z g: U, X2 K+ X# p2 P, m+ a
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。0 m- n- `0 Y$ E: x# ~% `$ @
. v- V+ u3 u$ a& H) l) A! {; R2 ~
' e- U% a5 u, _% r) gstatic scheduler_functions one_thread_per_connection_scheduler_functions=$ l3 r, s) s; `+ X
{4 H9 `6 e. ^- E: m" |. b6 U
0, // max_threads
: @& C, B* Z1 E+ r5 y& g; ~7 i/ r NULL, // init
$ r- Y7 b) x$ h' x! S* y init_new_connection_handler_thread, // init_new_connection_thread
, a p, w' P' ]8 |5 W8 Q- Y create_thread_to_handle_connection, // add_connection- ] {' H6 e/ c. @% h6 S
NULL, // thd_wait_begin
& _5 I! K- ~8 d6 @( Z. Z1 I8 N NULL, // thd_wait_end- d# ~5 ?+ Y5 Y8 L6 w7 _0 n+ {
NULL, // post_kill_notification
% u: \$ L; ?1 |0 h! {! _6 c one_thread_per_connection_end, // end_thread
8 E+ [9 D& V' P+ Q5 A5 u3 _ NULL, // end7 X G$ ~: T. y. s9 T
};! ], F. p8 R8 ^$ T _
* k H. m, k/ r2 n7 p
0 a; \. {+ e- M从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
. @. |8 _) l. ~' s+ E- a \" ]7 I% g8 y; A
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪, d% N7 s+ m7 O' ]+ v/ M
3 T) d$ |4 p* K( ~- Y" g V4 Y# J
void create_thread_to_handle_connection(THD *thd)
( X4 B. @% Q& x( n6 x" e9 @{! y7 @+ M" w% g4 g9 I1 t7 }
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib," _( F% G+ M; u4 d9 \0 z0 R% v6 r
handle_one_connection,(void*) thd))){}. r! N1 n4 ?4 x0 q! R5 q" ?
}4 R% M- a2 a! G
//触发回调函数 handle_one_connection, W2 @: R: x: l4 _$ E
pthread_handler_t handle_one_connection(void *arg)+ |+ I& n# s" }$ C# Z& `* K. j; f
{
4 u+ G3 R6 |* X$ }5 E# i9 i7 a/ E do_handle_one_connection(thd);
* l \/ u6 k# H: `}$ O% e7 i+ M% A; r3 W1 |" Z
//继续处理2 ^) b4 ]5 i" |
void do_handle_one_connection(THD *thd_arg){
0 @0 {7 q! ]. V( q while (thd_is_connection_alive(thd))
! y2 q- G, F1 o g9 z3 [% H {! K; t# A" v7 N
mysql_audit_release(thd);1 Q" { Q4 w3 o% h8 `
if (do_command(thd)) break; //这里的 do_command 继续处理" K$ r9 @, P. O2 V
}
6 ?6 M7 e. ~' q7 V# j# s( }}% F# s5 \! |9 U, H
//继续分发 O Y7 s- {# c$ b6 s
bool do_command(THD *thd)
$ x5 E9 Y( y9 b{
. @* a9 t) o( V, o. Y) f P return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
5 p: \& D0 c6 [: y}
& `* M, t/ q0 Jbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
* i {' @, u' y& r. S; M1 a{
$ h# Q) D" | F$ u% v+ T3 l switch (command) {8 y. r" e/ D! K$ Y* W, u
case COM_INIT_DB: .... break;/ T2 F1 C& V* [, T" |# R( {4 r$ D3 X
...
/ c' ~7 Y- x$ A4 j& O case COM_QUERY: //查询语句: insert xxxx. x+ b3 L: {3 @( q. K. \! _& O
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析, b+ x3 l+ `1 P: Q. \
break;6 x. T$ i+ K& _
}. ?& k% D5 c- \4 ~* a
}
* p3 I( M j' P8 b//sql解析模块$ x0 i) x8 i* c
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
, E/ L' }5 h7 t) v# K{
* ]7 |! g, z8 P" O6 v- x' T( d) c error= mysql_execute_command(thd);
! R9 J8 W) K$ a+ {9 v* F, H}
4 x' O1 d5 B/ Y$ k# M. C! k
/ @- B5 c4 [* j9 f3 t- L2 ? v. R9 y" b
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
5 R! |( q1 r$ S* w5 q+ d) S) Q* m+ ]
//继续执行% T0 }: u3 ?/ K# z! T
int mysql_execute_command(THD *thd)
7 O7 a# d" {" M+ F& `' K" H{
/ W$ g! t& w, {! G+ a switch (lex->sql_command)
' v7 w1 Z u% g# }# X {
8 s, D0 s# `) k2 R( Y case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;) ~# E" ?* Z2 f |% ?0 S/ M
+ L" D! v$ F, o+ [; @) E! O7 V2 z
//这个 insert 就是我要追的2 P+ X; z, R: k' n! b
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
0 b! |7 b# u+ ?: f( } lex->update_list, lex->value_list,2 H# u6 G! ?+ [ I3 x2 U
lex->duplicates, lex->ignore);
+ Z( M8 u1 i3 O4 K+ B7 O2 j# a L( E }; ?, I. v% U8 R7 I5 P' B
}
3 h& c9 a% H& e% t) Q//insert插入操作处理7 }& F' t; [6 m5 h& }
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list," d a# e0 y0 t# p( k! P: D: |# {
List<Item> &update_fields, List<Item> &update_values, / _' }" W, k8 w+ b
enum_duplicates duplic, bool ignore)
. `0 }* v0 f4 d$ N1 b1 k{
3 d: L4 o* v5 T% j" _) p while ((values= its++))9 P$ W, f& E5 U7 _/ Z, x
{
# i8 ^4 d7 P1 g* m5 ?3 h9 p error= write_record(thd, table, &info, &update);& ]; @7 y. |+ \) @9 S# {
}
7 E7 j3 \4 j, V, v& V}3 d i( W( N' V
//写入记录
' |* `# @% S+ V0 l/ W, `int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
4 W" g$ ?( |# K) Y' Z{ @9 l/ x. j% s! D% C* h
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
( K2 O: i2 ]" _; J7 Q1 o {
$ i3 F, S2 j K. r4 T // ha_write_row 重点是这个函数
7 Y' A% f0 l! g. h7 d4 w6 k1 r while ((error=table->file->ha_write_row(table->record[0])))/ s, o) ~8 ^8 |' M, V
{$ ]/ f, c7 Z X& n
....
# w6 ^; t- q' m0 h7 i! ?9 {0 N }- |! Z( z& ?2 D+ F6 Z o. h
}
" ?) ]0 x8 M. f1 Z$ K2 [1 X}
6 Z- g0 p0 E, |6 y' M. @5 B& o% b# Y! {9 H! I
0 y5 I- O- j5 e; Y1 x
: k6 h& l. z% P4 _) ?' ]
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
; L) t, M& K1 ]/ S$ q" ]8 E& {
% `. u" M2 _! L<3> 继续挖 ha_write_row
; C; C/ r; r5 k6 X9 K3 ]" K3 Z" s8 F! u, E6 ]& v0 M
int handler::ha_write_row(uchar *buf)5 V% N' i' ?" | `% h
{
( s$ y/ k4 K o0 k; Y MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
, `6 T0 _' `* r q9 j}4 _- C) i h& H* B* j
! e6 s4 p' _8 s, n; @
//这是一个虚方法
$ P( v: h/ W5 Y# W: m# ovirtual int write_row(uchar *buf __attribute__((unused)))
6 C1 B( ]0 o6 q4 n2 }2 }{) k9 |6 \. W0 ^: i
return HA_ERR_WRONG_COMMAND;4 Z% G" ~5 d/ V1 A
}$ r4 u/ A. y9 Y1 _) h d" X1 X
! y9 @, M( `, J! Z
( w0 t) B/ L0 u: b看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁) U; q: v$ t3 \6 J1 h
8 Q3 s% Y2 Z$ r8 V$ s
3. 调用链图
6 x$ p3 V7 f4 I0 F5 i+ c5 ?; _这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。; Y, W; @: \3 w' @2 j2 u/ B4 L* n
![]()
4 m$ z6 e1 b: v$ L7 v' B, b0 H) [4 n) _, V
, ~, o' b' H6 a! }3 E
三:总结0 o; E$ }+ ?) ^3 ?3 X7 i+ p7 p
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。+ n8 t( [& _2 ?" a9 @
————————————————
) m% T# \. v- I, h7 g+ y+ c0 \版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
! [' X, \) r& c) c原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415( }* p, @8 ^8 {: K% A5 h
|
zan
|