- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55582 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17626
- 相册
- 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 田老师国赛冲刺课 |
一:背景3 C+ `( S5 L5 [, `7 p* A: ?
1. 讲故事
2 c, s, l S! v0 c最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。6 a' Q8 k2 I3 p9 C+ ~! b* s9 o3 p
- G+ `) Z% N$ f) S; n( S* l7 n8 ]0 \+ T
二:了解架构图5 H& @) o8 [; T0 P: z
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# y9 O, ]# h$ D# q+ x# y: \: V
% a9 k1 O$ | A0 s! r1. 从架构图入手$ A, G" O; {" c9 j j. H) A) h
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。' z3 Y; X6 E/ F9 E6 Z7 Z* b" a
![]()
9 k" r& ^6 P: E% ]' U% o# r! T" Q B$ j. B
' \6 F0 {. W: ~5 j' v% c* h& }
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~* I: O& J! g6 y0 l. O
! O" t6 t. ]7 s9 s5 h, I+ m1 \; I
2. 功能点介绍 [& O8 y- j3 H! L, }
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。& Z5 b& |+ v$ J, l& j7 @7 V
3 d2 A/ d( L- i<1> Client
( l2 n# |) J; M5 O( ^$ B不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
/ P) Y- t2 i: r y0 [- M1 h4 C& j) c4 N8 a! [9 a
<2> Connection/Thread Pool
+ Z5 Y) E, j/ f2 f8 c tMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。& \! m5 N& j" _; T# E! i
# D/ p" w! g# J1 d- [# c<3> SqlInterface,Parse,Optimizer,Cache
' o+ f* I& e F$ L( ]对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
9 @2 K( U/ B* V% y3 _7 l) x9 u7 {+ k7 K
<4> Storage Engines6 h) I+ @ `+ Q v
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。 Q/ Z( S' t" t
# R) b1 L9 L8 F( Y3 }5 s
三: 源码分析
. g! a* A) `, \* A" L- i关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
1 K+ r5 Q+ j+ M; T2 V! g" B+ X9 W& V* P& d
1. 了解mysql是如何启动监听的; }* q( I# |! v6 y
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。. M* A* [) Q1 U8 I4 w' |9 Z) `6 s
![]()
6 I- N; b: V9 [" K; k
# y9 I4 n- X2 V5 Q1 }( F6 p3 m. F6 P7 F7 o' }
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。, G# `( h8 ^' T
# U* c! v: C, U7 `<1> mysqld_main 入口函数 => sql/main.cc
# W# W0 `- b5 E' P" c$ {. O! ?3 ~- M) |5 w0 O, d+ B1 v
Y8 J/ D; c: e- _7 o3 p" `4 l; _extern int mysqld_main(int argc, char **argv);( D8 J9 c: X* l- Z
% G/ G3 ?; E5 L" w4 N8 V+ vint main(int argc, char **argv)
* C. h/ c# \/ k{+ R8 f) n, s1 t" B, N( s
return mysqld_main(argc, argv);
5 g9 Z* q" ]+ k( ?}
+ p2 ~2 \2 g5 u0 r0 r0 P9 @
+ `3 H c$ m {; v' m* e; Y" r. y# v' R; x, i' ^" X/ q, U/ X) P J4 O$ a
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
/ W& e$ s" I; q% ^/ m& \& R; |2 b' k5 l
<2> 创建监听
5 p9 ~; d; g6 y. g) |* W4 j& R* G3 c1 j7 u. u3 w( z
2 c/ m3 r% F0 C
int mysqld_main(int argc, char **argv)
E3 |8 T" I3 ]0 F& d/ \2 n' j{: P; N: V/ ?& e. o. G
//创建服务监听线程
0 ?" u$ l% | R) F4 ] handle_connections_sockets();! y5 n" C5 h- I2 g5 S3 h \
}
) j6 m6 w" P5 O4 C3 k
% [" h1 e& [6 p7 g5 i; y/ nvoid handle_connections_sockets()% E3 e: F5 N/ i1 ?. y- V
{5 a3 s2 o; D- Z3 a& ]7 k
//监听连接
1 d5 m& O: k! V# a) a1 |# E. q new_sock= mysql_socket_accept(key_socket_client_connection, sock,
: U% b! _3 x1 j% h (struct sockaddr *)(&cAddr), &length);
" v/ z/ T, w4 O2 s a- a% U/ G( i
, X+ i( E. F @* f! m/ T/ g if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
* J/ N7 h) N) C thd->security_ctx->set_host((char*) my_localhost);
: ^" ^. C7 P Q: @/ i( f; l3 E! l8 [ {
//创建连接
/ L3 A& s) W0 \% g+ p$ F+ A create_new_thread(thd);
' s1 @( h& x5 V6 @8 ?# T}
5 j/ }; F( e& y6 V) ?/ S5 e9 e" W" k! X7 |" G
//创建新线程处理处理用户连接" U+ H7 q. a$ N* c/ l! h4 Q
static void create_new_thread(THD *thd){
1 o2 i, A) P6 i7 k, ?5 t4 ?
% W+ W" Y* B+ I thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;, [* m' D" h j. |4 F+ Y% K
1 l/ r6 D( T' N# e) I( s
//线程进了线程调度器3 {( [( l# e% f' D ]
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); % m% } M* _1 c3 M, {+ u
}
; q/ P* C2 e1 {/ y" r2 b. K9 ^7 P. K' f" w) I/ Z/ q" D
* R }2 i/ a- ~+ Y4 @3 r- J& P
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。0 _, H" U! v4 R/ j
/ G& j0 z' v$ e# L# j W+ H' B( i* P: y# r! w6 u
2. 理解mysql是如何处理sql请求
; n8 W& s1 R7 _1 g* n! K M这里我以Insert操作为例稍微解剖下处理流程:+ |# L7 ^; y, h0 N# t8 J$ m4 i
5 O3 f7 Q: ]: a9 w9 `
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
7 n( J) I" Q: ~( N2 J a, C
# w2 H2 t# _' \- I# e @1 ~$ |
3 g* O0 a: L7 T& sstatic scheduler_functions one_thread_per_connection_scheduler_functions=
, i% _+ j# Q _& ~8 U" Z{
$ l( E) t: _0 p9 o: L9 O 0, // max_threads
9 H8 s: o3 n1 q6 c# K NULL, // init
2 |$ a$ I# w4 @* O init_new_connection_handler_thread, // init_new_connection_thread
2 {6 P& |3 P' O! T create_thread_to_handle_connection, // add_connection
. b* K9 R+ `, W( b6 n7 K. l/ |& D NULL, // thd_wait_begin3 z, ]+ e4 L: X6 k
NULL, // thd_wait_end
$ C: n" B- O6 p8 m9 x NULL, // post_kill_notification& j3 J, W# N ~5 X( Y3 m8 u) Z
one_thread_per_connection_end, // end_thread
, u" p* L% i2 {0 m4 ?5 P# S0 B NULL, // end
6 P3 s8 z* Y" W6 B: j0 G};
$ O% T6 M3 }( `" r$ C8 D, ^ h3 \: q/ o
; K! L2 s- Y+ g& Z. Y+ y7 Q5 H从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
% { D& g& m& s
: I9 e/ K0 t l: x5 p# ~* v# `<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪* S1 ^+ u6 R8 f9 r! j& N: f7 A$ t* [
! }9 G& n; J" L: \void create_thread_to_handle_connection(THD *thd)9 |: a; t! i' ~2 S; a" V/ \
{6 S v6 N' O& ]8 V$ @
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
$ [& w b. V8 } handle_one_connection,(void*) thd))){}
; }2 Y4 D w# e2 L6 c2 T}
/ w& w* y" `% W# \6 P: q//触发回调函数 handle_one_connection
6 s; A& X9 r3 d3 c5 Jpthread_handler_t handle_one_connection(void *arg); w* ^9 i' p4 ^( I+ e" u) w
{
4 U" P' W* ~9 A do_handle_one_connection(thd);
0 b( O8 M$ b( k/ i7 ?! s}; Y: ~) K$ j+ P: B" Y( L9 J
//继续处理
6 I4 o: i3 ^6 Bvoid do_handle_one_connection(THD *thd_arg){5 @, b) m9 ]5 q
while (thd_is_connection_alive(thd))
3 G+ a# E5 W) n3 k3 [. {- | {
: L* B% c; P5 F mysql_audit_release(thd);
2 `1 _) N' N' q if (do_command(thd)) break; //这里的 do_command 继续处理9 l4 [- ?. x; l* u2 k$ y) |$ F
}
7 g8 Y( f1 c6 _% ?; S8 e. Y( ~8 U2 C}+ X" o/ S8 x" y: F1 g* T( k# h" l
//继续分发. |$ c# M+ n9 F: X; b" W5 ?
bool do_command(THD *thd)
- ~8 v$ u3 ~8 m) d{
0 K7 F# L' r9 w7 l return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
]; s, z: K C& b% l, z1 R}" N3 N; q8 d' s% u3 g2 ^/ A
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
, w& P9 R; h. O! C1 l{
+ |! `+ I: A4 e% X switch (command) {
* V3 f( L8 m: E/ `( `1 ]) ? case COM_INIT_DB: .... break;
" R/ B; n2 Y% k: s T# R, Z ...8 M% E4 [8 f4 o0 e7 B V
case COM_QUERY: //查询语句: insert xxxx
5 B: o& A3 u( H9 ]' {: D8 C mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析* `1 F* ^2 m2 j9 X" o5 B9 G
break;
% _" d/ R. ]. D8 \9 S' X2 a$ p }5 Q9 P6 g$ ^7 c& s& ~ E9 h2 ~
}
. |- v7 f5 H* K//sql解析模块9 w h* K. n4 G, v w2 k( t
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
8 }2 N4 W& N; J6 g/ R3 q8 B1 H{
4 C+ D9 r7 j/ C K& R6 ? error= mysql_execute_command(thd);
3 U0 _8 Z; K% u; V8 L}
2 n3 l3 ^# O0 w: q0 j: t+ O6 M/ j; {! Z+ P: ]/ Z
) n, A% b4 B0 }1 f/ n2 R0 ] u<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。: V% d: S0 _* w9 s% E( h; Y
3 g% r; W' t! z! C! g
//继续执行
8 p6 P+ [2 l6 n: l% J5 g0 ^int mysql_execute_command(THD *thd)* Z0 E6 `$ l" R: g, D$ _
{
$ X: s! U: q; S switch (lex->sql_command) 8 Y- r0 r' k7 K+ Z4 A: B' h
{/ }: J3 L. G3 ^' n. V8 w
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
+ ^ y* W# C$ a/ J+ ^1 F7 \# X1 j
//这个 insert 就是我要追的) I$ ^, n8 M8 ^8 ], L
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
, i# y8 T! u8 y" a$ j) ^# Q( s lex->update_list, lex->value_list,
) [. ^" l: g, J: ?. ` lex->duplicates, lex->ignore);
6 h/ R( x$ g' ?- C/ b( k }7 z6 r/ V: |7 l) s+ c8 b$ |
}
% \1 z$ ?) `: Q//insert插入操作处理
; [' l& c* _0 D7 }$ S$ D5 Cbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
% B9 h# o: ?: x! U0 ~ List<Item> &update_fields, List<Item> &update_values, ! j+ v m! d( E; s9 x k3 Q7 \
enum_duplicates duplic, bool ignore)' r! W2 O) f1 a7 t" A
{- P+ I, I+ v" K6 c( C- m
while ((values= its++))
+ C6 A/ X1 B- |" o7 V$ ~1 p {
: O) E5 x3 ?/ k8 r' H. s9 S2 W error= write_record(thd, table, &info, &update);
% x0 `" k& z# Z. V) ? }$ v' w! ]# L& [% k
}0 [& ~6 Q, U6 ?8 [" D( u4 N
//写入记录
. V* j- M0 Y6 Q& V/ }int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
, d. Y0 Z x/ G{8 x' D$ k! \# ~: e; x
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
3 W0 q( }# `" e7 n5 ^ {
/ n- }8 O, f( r) q! i // ha_write_row 重点是这个函数
' ~! D+ f. X+ u) x. L while ((error=table->file->ha_write_row(table->record[0])))
8 e6 Z- Y. N" I! w {
8 E+ Q& l8 [7 c/ c! y ....
; K4 V0 e7 l% }8 C2 b( R) ` }' }# e2 i" E$ S1 F8 y. O
}
3 | j( c+ c& v4 }* Q}- b4 J/ z$ H: {9 a8 T( V; V
. ]7 {! u4 e6 h
3 j: A _' V: s" C# [7 ~. N0 i4 |
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
* U& H7 S. t7 U4 q) s _8 i
3 W: V4 s' p$ L% `1 i$ @<3> 继续挖 ha_write_row
7 {7 k% X+ x+ W* g' h% k3 B2 o" `! N6 g& ]2 u2 V: N
int handler::ha_write_row(uchar *buf); y9 i# u7 X3 o. ?1 v
{0 j; X$ n7 q+ ^4 }9 ]
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })+ @& f2 g2 v9 }1 ?& I+ g* g8 w
}
. R3 h7 M& T, ?* N% i8 P/ B
4 j) J1 W- ^7 b/ Z/ b//这是一个虚方法
8 H4 J! {; X2 g$ k3 B3 |virtual int write_row(uchar *buf __attribute__((unused)))) _) q" U6 b/ e7 J6 Q9 Z; t' h8 S
{* S' s3 y$ i& S$ i
return HA_ERR_WRONG_COMMAND;
+ p6 s0 H: B$ i! `& e0 Y: A' m}
- A: k. y) X# i' n8 _
6 l5 X) S; @7 e$ q& Q. z& T! a C/ P/ n
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
$ K- F" J% ~# Z* Q" S( \
) R' a' a9 t5 ^& x4 D3. 调用链图
! B& p7 i0 `+ |/ `" M这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
- V+ Q8 J% `* d) U# O8 p + w, T" A" r2 r( M) L. U
4 i. u5 m! a0 h8 O! Y# K/ M
$ t1 t" p) D6 w8 I: d3 p8 \- o三:总结" O3 H- y! ` D' z
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) a, b, }/ ^1 h& T' K
————————————————1 P% B$ P/ L2 g a* q
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。9 c- ?4 ^7 i2 M% h
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
& d+ H% `1 x$ k |
zan
|