- 在线时间
- 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 田老师国赛冲刺课 |
一:背景
( L5 O! {8 s- o5 Q1. 讲故事
2 m8 G2 t/ ~) Z最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
# Z8 `% b( r6 w; S3 g! ]5 d- D* g' j* Q: o/ m- s% Z1 E1 ?9 E
二:了解架构图* O% r4 m# N4 `; U# @/ _6 M [
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
1 f+ F5 }* ~1 q# e
" Q- r5 }+ l! D- a8 ]1. 从架构图入手
# W( d# i( z& j. @大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
. k& P$ M! ~! N" s5 a7 a/ e. a & {6 k4 ]5 s& W$ S1 q
8 X: {$ M" V6 }
7 ^( [ p8 o" m9 V其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~% ]/ [$ n, L: ~! K2 y: f# C
1 `+ c3 u+ d/ e: y" L! c) K2. 功能点介绍* i- J- t) _) S! r% g: [+ x
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。' D- O- Z. @8 \" X" ?& E$ y0 V
3 J" N) N7 s6 b6 c- K
<1> Client
9 q' Q/ _5 Z0 T: i* U. S不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
! I7 s" L6 G: d; x/ `# u
! S @. m( G2 c+ n2 R<2> Connection/Thread Pool4 I8 p! B) I1 E. o
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。( J: J% P* p9 Z$ v
2 X! q7 J2 n3 b
<3> SqlInterface,Parse,Optimizer,Cache
' @# U3 J0 e8 e4 z对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
7 X' L6 z5 w, Z/ P, o; i9 r
3 [8 R$ T9 n6 f! T# M2 b& @<4> Storage Engines
/ Y0 t D3 [$ c4 S* H1 a负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。3 w& J1 l% {* @
! u1 s* O9 X* e6 U; r; S三: 源码分析) Y1 G! t: X( ?1 q9 G' L9 Z
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。$ }. ]" ^/ t: H4 F4 W& W
: X8 q9 H9 \8 H2 E
1. 了解mysql是如何启动监听的
1 a4 G. [0 z W9 s x: o& r# T手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
' M0 q/ V% g4 B- P 3 d& I$ T, M, `+ Z! q# @
7 O: {/ \ F2 L1 P2 |
' D! P) l4 Z6 s* _ n6 v( C从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。5 l4 w" B; V. X8 R. P9 a" r
& |: S3 z* r4 i& u<1> mysqld_main 入口函数 => sql/main.cc" d! ?1 ~' |1 F7 b8 p' Y/ A
8 A7 B9 _# T5 {
# G: B" ^) L" T( B6 s8 o9 xextern int mysqld_main(int argc, char **argv);7 ?7 S- l- ^0 q( u p! _
, S* G* q1 }$ K" l% o4 A# Qint main(int argc, char **argv)2 L$ ^( o B4 p6 ^: W
{
3 R; s: ]3 w& q3 X3 G return mysqld_main(argc, argv);+ a/ t0 x' l! ]* ^2 m& B
}$ B3 P' F C, ` y, \# A
( c" i$ i- D2 R9 H) h$ j) e0 |: V% b s- R
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。0 U+ y" c( F. z' J( K. ?
6 Y$ T8 j0 N- C+ p8 ~9 Q
<2> 创建监听& G& a' [$ `$ H
" x0 Y+ c* C2 S
7 Y# S- o, V: W5 V# p
int mysqld_main(int argc, char **argv)
. D! G) r/ j& ~3 G{( p3 C5 I$ J. W8 v
//创建服务监听线程
' t! C+ t/ z$ M3 _ handle_connections_sockets();- T' Y* @: ^$ i( s2 D* c) K7 a
}
3 I* g! E8 S+ W0 }8 d, o& z0 H% ?. f) C, i) H+ G# C- A1 i
void handle_connections_sockets()( r- f1 R* \2 C( U
{$ D; a2 K4 x% m& x* Z# |
//监听连接4 `, m5 B, d" W* g/ O
new_sock= mysql_socket_accept(key_socket_client_connection, sock,, B- Z- y7 o( i8 U5 H
(struct sockaddr *)(&cAddr), &length);+ @2 c0 F. ?" u8 j' j9 b- b
5 Q+ d) i9 h$ L7 E3 z
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
7 n- H4 ]% |$ s% B$ f( K thd->security_ctx->set_host((char*) my_localhost);
% ^# p+ k# f1 P( A9 n' i8 O( m1 S% `% V+ N6 c+ v/ ]5 Z
//创建连接/ X. R" u4 X& T: y9 f7 V8 D
create_new_thread(thd);
/ S* M) S4 U7 l8 K% ]}
& ]2 V! k4 u7 _% h' o
+ S- _$ w3 Y* I* N/ L//创建新线程处理处理用户连接
1 u$ H! [. s" z4 f! d# bstatic void create_new_thread(THD *thd){0 N9 L% Z. J0 c0 }+ ? F
" a/ t+ [- ]* V
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
' R! m# T& J2 R/ |8 @/ ?- u% }' u4 \: M( I9 ~
//线程进了线程调度器4 o; T0 n1 { w8 S
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
1 p3 R8 h+ s2 T. U5 [}" L" w0 k. Q ` O% r5 j ]. z" R
8 E/ U/ n x3 @9 |( }( {8 S1 P5 b; ]$ X" R+ I$ k$ ? U
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
1 P) i) C* q1 l$ u4 [. O. W9 U3 j
" X) y/ q7 K" O9 U- T1 x2 ?& ?( `
2. 理解mysql是如何处理sql请求
' S& E- Y5 l- y/ m这里我以Insert操作为例稍微解剖下处理流程:
. N' v7 L% Q1 |7 M4 O
8 _3 d3 s. Y- ^$ h当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。! {% T2 |6 C0 @: C0 w7 N+ p
% C. k/ c3 M; U C* D: U+ x
; K( x1 w2 e$ bstatic scheduler_functions one_thread_per_connection_scheduler_functions=
7 t- B8 o7 ]( {) J{
% c! i! c' q1 m- } 0, // max_threads
( J$ [* o. s6 ^1 B NULL, // init
4 J& d& C W$ k; R9 H& Z init_new_connection_handler_thread, // init_new_connection_thread
% b( q s( a0 D5 E+ U: q7 E# K create_thread_to_handle_connection, // add_connection0 d9 b* d% u- A
NULL, // thd_wait_begin# P! K0 x, ]- y$ E+ j- r& [9 ~3 |
NULL, // thd_wait_end
6 O1 N2 d/ M( C7 \8 _- [ NULL, // post_kill_notification
) o# Q* [+ n) o' j one_thread_per_connection_end, // end_thread6 {1 X7 i4 C. k. M9 B% n8 W
NULL, // end9 Z2 o1 T9 f( _
};+ c$ `3 X/ G, ~( M5 z; J( r
" W$ |1 u f# {7 d }5 Z- r
6 K A/ c/ F5 ~( }从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
* g# U( \: p$ |8 e5 _% c( p6 s1 ^8 |7 v# u! {6 S) l
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
* S& A1 o6 x) W+ R7 J- d$ A. R
?% a3 m; H$ \2 S/ Wvoid create_thread_to_handle_connection(THD *thd)
. o3 I& k9 ^% |4 g1 X{
- ]" p; O0 X& b6 }! u& x- Y# U if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
2 `3 G3 j: O& f" p handle_one_connection,(void*) thd))){}
, n! Y: k0 h! F" E3 h" \}, E* e& F6 h5 p* x& i8 H7 `
//触发回调函数 handle_one_connection
7 z" R0 Y1 ~5 Q) `) H5 opthread_handler_t handle_one_connection(void *arg)
7 T: `8 R/ G; Y) B{( J( Q; ?- {. D% a2 L5 K
do_handle_one_connection(thd);
% d% N- k/ @2 L! Z. ^}( F3 q5 z9 n; `& M% q0 V* D
//继续处理
' j" L: Q. I3 g; T9 fvoid do_handle_one_connection(THD *thd_arg){
7 n9 C5 V. ^0 | while (thd_is_connection_alive(thd)). f$ v# [, \6 B% |$ W9 o
{9 \3 P- y. Y! @
mysql_audit_release(thd);
: u3 t% W; l" _! T- R, {/ q$ M' I if (do_command(thd)) break; //这里的 do_command 继续处理
8 G1 E4 s; N+ h0 W& n }
4 C8 B* p* e" v' E# |}
$ q' e O( ^ {, a& \' m! d W//继续分发
1 m" Z5 v0 }0 C5 S0 ?& B' n4 `bool do_command(THD *thd)
9 {5 s! T4 u1 G{- I" [" o* k2 j& [5 L% {" Z! I, X7 f: T
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));$ s: V5 x- P' N( S* e9 V
}# g) ]8 K5 o, y
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length); g1 I# t% N s% j+ v/ e) n3 V$ U
{
. t% C2 ]0 j; v) L5 _+ I switch (command) {1 P! c: {9 V6 U& s
case COM_INIT_DB: .... break;! ~+ D9 Y. N! p6 ?3 w, d1 k- t
...
. q1 h3 Y6 T) P# X6 b) ?0 f case COM_QUERY: //查询语句: insert xxxx
W, v1 c( Y: S; B mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
8 s; w6 o! R. L6 b7 y% h8 Z6 F) s break;% G: C) Z* X4 [
}
! U6 l5 _9 x9 m& u}4 [3 l1 L R/ p' u+ W# I; e' ~
//sql解析模块& |# x$ f# N0 ]
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)8 j |' x7 X& C9 B! r. q
{2 N( O, c# K) o8 w4 Q
error= mysql_execute_command(thd);1 t' w: C3 I. Q5 L* N" \2 e
}) L; X( f0 k& e( P) r
e; C$ T, V5 T8 j; x2 C4 J. H
6 s2 ?1 A% [! V6 \! n E<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。 x Q- k, P( n/ X3 w/ v+ m) I
8 N* d& n# X9 z$ N8 q
//继续执行
4 v( @2 j* A) k. N: G% u+ Qint mysql_execute_command(THD *thd)2 U( i3 t2 u j
{3 H6 Q+ k+ J' w+ F- d
switch (lex->sql_command) 7 Q* p- P2 R/ E
{
- |2 q4 V( p" d9 m case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
" f( d' \5 u: `7 u. h( U" c5 x( X# W. _" v2 r
//这个 insert 就是我要追的
/ p& d- i u: ^2 A case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
* A1 p/ X9 S# `* T5 }# w lex->update_list, lex->value_list,; E$ E1 @! L% z" A: X7 e
lex->duplicates, lex->ignore);
4 w( W2 S! a0 x+ ~" a }
) h, B. B- J, H5 h: L8 p}
. Y) S1 e9 J! h# n//insert插入操作处理
/ ]+ M( w3 G4 T' h0 ~+ nbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,3 Z6 q3 I5 P( D J9 W* r; m) E" c
List<Item> &update_fields, List<Item> &update_values,
% z9 ?+ f4 ]$ u' B enum_duplicates duplic, bool ignore), ~ A' C4 h A
{
+ d) u9 n' X) R9 O# S while ((values= its++))
) t* h) L1 d, k* F {+ W6 _" H- B! w J) P
error= write_record(thd, table, &info, &update);- Q7 Z- b: R4 c' o6 g
}
* W. v2 v! l, a4 ?8 d}
f: i' a$ x# f3 R$ n//写入记录6 R' ]" }1 f9 G/ Y* }) K! J6 J2 H) _# Z
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
* n; w: \7 A5 r; W/ p6 m6 n{
5 T9 o! \3 `+ e) ` if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)% M8 V* p+ w' h) `2 L- o
{
0 i G: T' M% n& ? // ha_write_row 重点是这个函数2 \$ }1 \, |6 K- j' J1 J
while ((error=table->file->ha_write_row(table->record[0])))
0 b. y3 ], e2 x" B& R: w4 d3 L {9 t: i9 _3 i% H9 B5 k5 m
....+ v8 G$ L0 |% S- |
}; A& m! q! @2 N9 n6 l" T
}
0 ]* d. Z) k# Y( D4 G}
+ A! i, B6 l* E, G/ X! E
4 Z! i) Q7 v3 B2 e! U' X% P; B" I5 Q0 _6 X" s: h/ k7 i' T2 f
8 \' b/ I: N7 O( P* r8 j9 j4 m6 @/ j
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
8 c. i8 R( t6 w4 A! L( o
6 F1 D* O1 ?7 E1 I0 p5 Q' R<3> 继续挖 ha_write_row2 b$ ~' R1 {( K: @' K0 T+ i
! P) u# s% v) i# @0 Z' |int handler::ha_write_row(uchar *buf)
, N' m" ?" M, |) f1 ?) u{. B; k( \6 O! z' l: V, l4 ]4 A
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })9 u: y' q3 g' ^ ~
}, C4 ^$ K) |% p' b3 v
$ u/ H: T" P/ @5 O# T+ V% Q. e//这是一个虚方法3 {7 S4 M5 k2 ^1 P
virtual int write_row(uchar *buf __attribute__((unused)))
% J* m' N0 Z, r' l{
! S& z, ]: O5 P" w7 w- y, X return HA_ERR_WRONG_COMMAND;
1 V9 c% M* n+ t% y}5 ?5 j4 |0 t7 d2 j3 F" b
1 |0 i, \6 L5 m2 }; x3 B3 b7 H+ c$ ~' c' j9 ?* b' B
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁( f& q& _4 q$ \+ i$ Q( Q2 r& b
$ E' X: E" t8 x. P& D+ M1 {4 Z3. 调用链图# w' j% w9 T9 Q& e) B
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。, U. g) k$ D, d4 D2 {
- G8 f! T5 l3 R3 _& R
. O4 _: d* Q1 L! W* B4 \
6 y1 A1 C5 D! Q. _+ l三:总结( @# s5 B3 [* s9 l
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
& X. _0 \7 k3 c" w$ p: G————————————————: m9 {" b9 X N
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
( |6 [" O0 m% Z) }( T) L& C原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
9 j9 x3 L g; U3 Y3 x ^% T" L; Q. v" J |
zan
|