- 在线时间
- 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 田老师国赛冲刺课 |
一:背景
7 E6 x4 t' l t3 e3 o. }7 M1. 讲故事
! p& V% h9 T+ C2 @$ u1 T最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。! i6 e, v* J! o2 \3 A
6 c; L, Q, ?; |
二:了解架构图
% K* H+ Y, t: Jmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。; S+ i2 H0 @, t" q6 [8 d
0 K b; l* k4 b+ c, V
1. 从架构图入手0 @- d, U' @4 w8 R+ T6 }
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
1 r! X0 Y# g+ u9 ]( D7 a1 I# T![]()
) J/ h3 P1 m( B: U% E( I
\, x4 K% z; z8 @$ Q# q- e
4 ^0 F3 t+ g/ s6 a其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~4 A6 i7 A7 j0 P6 ^! \
2 ^: M7 r0 e% n5 z5 H2. 功能点介绍
# z- l' U! g7 I& v1 Y R7 ^MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
% P9 I9 o5 U- r4 e8 B& H* y8 ^( Y. G4 z! I: I" f0 C
<1> Client/ @( o0 p' h2 ^" V
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
/ B) G% e, P5 [) {$ v5 e& h/ b$ P! j3 ?0 f! M
<2> Connection/Thread Pool7 @; f9 |& I( C/ U* p. p, Y; g
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
U; X: f7 y; C; h9 [ f% u0 \1 N& e: o( h3 @! M+ b7 f
<3> SqlInterface,Parse,Optimizer,Cache9 s( h6 B9 C5 ?, S5 d. L/ C: } P
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。& _5 O1 }2 o0 ^ h; ~: H# e
- C3 L" d# N" [2 Y" p<4> Storage Engines
# u+ A' p B0 y' D负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。6 q) b- F2 o7 k2 T2 T1 }" T# R; D6 G
( M7 `) K3 R1 X( \8 n
三: 源码分析
) i, j3 O7 Z" c+ \. f/ c关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。6 t0 s2 A; E p
) w L% |2 e; t' m/ C# Y, n6 C' O1. 了解mysql是如何启动监听的( b" ^$ [* F2 K& h( |+ l9 l o
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。* e6 r7 Q# Z7 Z5 h8 Q" ^
![]()
/ x( K6 e9 w5 M; a, q$ x( j
* A% |* x- ^3 ?$ L! _9 _4 E5 P; ~% Q1 H0 o# R# m) j
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。: m/ Y% q- ]& i3 j
u. x6 G. M, d: k- S+ l<1> mysqld_main 入口函数 => sql/main.cc
/ K: G( @+ R" T* [3 A0 Q& ]' k& ]7 M5 |* o5 Y% f& i
3 R$ }9 r! [1 d* ?
extern int mysqld_main(int argc, char **argv);9 o- l- F1 h% \- v$ M+ K
! M \" z- H8 K: g! b4 J9 Z
int main(int argc, char **argv)
* `; j' k* z2 ^& o& o) d{
, a4 m& E0 G6 }1 E, F/ n return mysqld_main(argc, argv);2 _# Z! s$ |- C; U, Z! w; R( W. q7 R
}$ c" r1 ?% R7 [5 ?4 E* A. j
0 i+ k$ u8 }) j; I) F2 O* Z' S
/ F. A; ~3 n* P/ c$ j7 }这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。# u" }( I- h4 @
: ] ^3 b! m* q3 l" T) Z<2> 创建监听" }" q4 t- ?4 a3 b# F% @& q
9 ?7 F$ M4 p) N* W$ a
/ k5 ]2 ?1 H/ S1 g6 U) Nint mysqld_main(int argc, char **argv)
2 J- L! g% [ y+ J+ ~{1 G* a- B1 K# [) c9 z; Q
//创建服务监听线程! z' f$ t2 u- `# A% [1 _
handle_connections_sockets();
1 O" m8 a0 {/ D) A1 t6 x$ h, Y}
! \% r5 s0 m" q, ]+ ?, }' K0 a5 w0 Z Q
void handle_connections_sockets()
! k8 Y2 c) h9 J3 ~5 `1 q- k{
T! g% n9 q% p) F; e" p4 V //监听连接: b6 l: I) y- J- y% w* r, O- ~
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
8 ]* `9 M5 E: C! l: x0 d (struct sockaddr *)(&cAddr), &length);. G: e, z$ k% Y9 A9 ~
: l0 P6 d0 o% P! G' U' @9 [, g if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))4 |% d4 `% k9 u2 t! c
thd->security_ctx->set_host((char*) my_localhost); p1 i, u9 Y$ ^6 Z
2 V+ [) k& o% j/ _
//创建连接. X" E9 Z+ V' e/ d, }) Q
create_new_thread(thd);
8 V* b5 ]3 P0 `: E6 w}
5 J# N- ~' Z: a3 O
% K0 f# {1 K) r: |) m5 Q8 t9 d" }3 C//创建新线程处理处理用户连接
- {' C+ @+ o* F" g* o" {static void create_new_thread(THD *thd){
5 z9 }/ q' A5 p; [7 _" T# m/ B( c8 t0 L! Z; q0 w$ F0 D) i' I0 R3 I1 {
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
& X/ c8 Q8 a; l+ g) L( R
4 M; l1 E3 o, n) X6 y% y //线程进了线程调度器+ H7 v- H9 l& D2 y" K( }2 {$ P
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
0 F# p: j6 N8 L# z P}
6 o( `, u# F0 \; G8 B4 g- j: i0 w* `0 K
; |! ]1 f( A- ~至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
2 `0 }; H! c6 ?( ~$ h) R
z1 R/ p) `3 ^, s! G. y) S
0 O7 a" V$ ^" d3 D/ z2. 理解mysql是如何处理sql请求
9 `8 I4 M- B( c这里我以Insert操作为例稍微解剖下处理流程:
4 x5 w! S* b3 U5 r4 R5 N! M) r0 x, c5 X2 g# | j
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
: a* U' e! F( Q6 N& d% O0 y
5 L0 L, p, P7 ^+ ^" H! c
6 w# B9 o% Q! E( c8 d6 L, ~static scheduler_functions one_thread_per_connection_scheduler_functions=
2 A3 [8 B) }" U, c5 y! N{- h* x# o/ W+ c8 W
0, // max_threads# Y$ [# {& c+ A% l' r2 B7 v
NULL, // init
' S. u7 ^2 S$ w9 V( Q' i3 L init_new_connection_handler_thread, // init_new_connection_thread* w. |8 N7 M7 ^; F, G4 C0 z. h
create_thread_to_handle_connection, // add_connection
4 K; U* \. F( x# ? NULL, // thd_wait_begin
1 k( B$ g* U( _" k, A' C NULL, // thd_wait_end
* f$ }- l) T1 }; Q; V1 g NULL, // post_kill_notification
2 y3 P4 _: z" T, _7 T; d+ I one_thread_per_connection_end, // end_thread
f1 {/ V& N' }9 O NULL, // end
4 `$ M3 m' M! U' {* X; X5 l1 W};
3 Z+ r" w" f, ~
]4 R3 L/ D( K" h. ^1 R, H7 M5 m! [5 G' C- g) O" {
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。2 v. l5 o7 @. c/ f
) c/ T4 M, e* F% D3 t
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪1 _0 U4 q% z6 s3 ~# k* ]4 S
( ~# i2 z$ Y/ D" d ?void create_thread_to_handle_connection(THD *thd)6 k* U" H; b0 _$ X7 r
{
7 r+ y1 c* Y$ x$ R% p) n if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,5 A ? c3 ~) _. i C+ S$ J* I
handle_one_connection,(void*) thd))){}
2 |: M& P$ x4 W* j. N9 E! q}
9 I% r) n# i/ Y T" n8 T4 P//触发回调函数 handle_one_connection
/ J# L0 J6 D+ b0 {3 wpthread_handler_t handle_one_connection(void *arg)4 L2 P9 M# I U! R6 E5 A
{
2 W# `& W- r) S5 @. ~/ e0 e do_handle_one_connection(thd);
/ v3 }: K3 v4 u' w- H, k}
4 i& h% V5 z+ d% [//继续处理' v0 L& V3 _( }5 L3 m
void do_handle_one_connection(THD *thd_arg){
( ^2 l! _: g v0 q4 n3 m9 [4 { while (thd_is_connection_alive(thd))
+ F$ b' B& g w# L/ X {1 P) \* k; z4 f* H5 z4 R
mysql_audit_release(thd);
: }( E# A7 |. g+ h( k1 T, { d+ f if (do_command(thd)) break; //这里的 do_command 继续处理
/ I% l0 j: y! t% x9 W0 C$ G+ P }5 s" }6 L* q- w8 r9 ?: ]$ S
}
* o9 R O) P3 G$ K//继续分发
3 A( U% f& V5 d v# Z( kbool do_command(THD *thd)
1 P$ r" \8 ~9 w: s{* i( _ K/ C! Z5 C8 ~
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
3 j; h5 d( B9 Q8 X1 E}
; P* [/ U* y5 ?: _bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
6 u, s5 ` \1 M' }{
; R8 s# V6 M: F' E! f) O switch (command) { t' R! F) L- ? x+ O/ ~( v
case COM_INIT_DB: .... break;9 Q9 G: k& o0 `/ ~& F
...! O3 K5 C# T! y0 q
case COM_QUERY: //查询语句: insert xxxx
6 p' C0 A. U# D$ ^8 a mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析' X& K9 A& \6 M. y9 g
break;
7 t% e& H* \$ p) Y1 j6 a }
+ X; Q) Y4 Q/ U1 P) {6 B, N}
; L4 o3 q8 O& r* Z; K9 T3 Q- G//sql解析模块
5 H- u4 W5 A9 k$ rvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
- s6 N" ^/ Y7 b5 p E{4 K) D+ h- i8 {( D% T
error= mysql_execute_command(thd);$ M- L5 J) ]0 }( ~" I% j
}! [9 y+ D' k% Z% Y7 L% p% Z' N
9 W: c. V9 c& g
, @, {7 D, z8 f. c) K2 \$ L* k( H
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
% @) j( a) f8 x* _; t$ L
7 `: ^3 c/ I+ Q+ X) Q7 u" v) a//继续执行* f$ S* L5 n3 ^
int mysql_execute_command(THD *thd)( S; R! A. l4 l
{
1 g' k5 N# r( V, U3 N switch (lex->sql_command) 0 s" V1 J5 Y9 |* ^; F
{
. w$ C! @5 {# J$ ` case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
) ?7 u' U0 ^, r& m7 w0 u0 R- h8 |- h2 n0 e5 u O; u
//这个 insert 就是我要追的3 k% m5 s% C. a8 V
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,/ j3 Y: B5 `1 e* T( C. I+ a
lex->update_list, lex->value_list,
8 t( C: U/ y3 c) a; o lex->duplicates, lex->ignore);0 h. Y) L* C ?' E+ t
}3 d9 X/ M" ]4 `3 Z0 u; @
}3 y3 _: e3 Y; c% i( I8 ?
//insert插入操作处理
2 x& x- [7 K# H4 m$ Hbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list, S" Y2 X# y# h6 [
List<Item> &update_fields, List<Item> &update_values,
: T3 o2 q Z& x6 n enum_duplicates duplic, bool ignore)
. |1 P: Z8 O% d. B, X{7 u+ b8 Y$ E' A! m' N/ e* M' [
while ((values= its++))
2 Y5 H# c/ {( O7 e+ N9 H {
9 X1 L* U; y- B$ ]# G error= write_record(thd, table, &info, &update);
% U) O8 I3 h3 U+ ? }3 f9 p8 H2 o! }1 q
}8 ]: M; b. C( o+ P& L A, p
//写入记录
# ]0 Z% H& ]8 eint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
0 [1 @% }* ]# }3 g{
. j: H8 m& Y4 ?; L8 M. `9 Y ^. D if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
; @( d3 Y: s& z- R% H/ q$ f {5 r) n H1 L$ ^( y3 A( ]
// ha_write_row 重点是这个函数9 \6 K5 G8 d9 c4 M$ [1 O
while ((error=table->file->ha_write_row(table->record[0])))! @; h& d; c/ `, i0 Y1 H! x
{
5 k7 o7 Z5 t# S3 T! r( g ....7 g7 l( D0 C; I \# \/ I; p
}+ B+ B1 C; C8 ~( g9 ]2 ]" c
}
: I- O6 n$ `8 B) j; `# V0 y} s |, f4 _: |* Y
" n! g6 A3 J& H& N# ?' x5 [
) J3 ^. f! Y' Q) S: b8 c. S8 z* D. s2 \$ ]
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
' j* J; R" m( x5 b1 s: f% x% @2 x9 \( v; s5 O9 X7 x
<3> 继续挖 ha_write_row
; o$ H8 R9 `; `
2 j: h+ h$ J8 K2 w6 ~1 H$ gint handler::ha_write_row(uchar *buf)% |8 y7 B- Y& k: @: [
{
3 g6 z0 {& P, ^ @5 _' z& n- L MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })$ }7 D( R a! O3 f! q
}: C3 w [! g9 P- u/ b5 L9 Q' c
) G5 j; S1 R& z
//这是一个虚方法- j h, n' l# ~ ~- W2 V
virtual int write_row(uchar *buf __attribute__((unused)))5 U' Z4 f$ F/ v5 U+ u
{1 a0 Z! b# S. g% E" c+ L
return HA_ERR_WRONG_COMMAND;
' _; U0 v- r1 a}8 I& M" ]4 g9 N$ I$ h
. t/ q0 ^8 \ B% W+ I4 T5 t Z6 J& j% E. T I
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁) R7 J$ F& j x- I8 P
* J4 z" `0 Z0 z
3. 调用链图
. V B& B2 W) V1 m这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
' t+ v3 G4 _( K+ X$ |& Y+ Q* g 8 I5 O/ o; d# R
5 P# l' K- ?* _! o
9 C. E' V% n1 b( X: j7 Q, U \
三:总结
" w+ R( C7 L; l- o大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。2 q$ Q4 H R* ^* t, R/ z
————————————————; u# [) h1 t. \4 i) h' B
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。, V0 j' B$ f9 _* ]% [
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
w6 A5 L' i* L6 x9 [ |
zan
|