在线时间 661 小时 最后登录 2023-8-1 注册时间 2017-5-2 听众数 32 收听数 1 能力 10 分 体力 55507 点 威望 51 点 阅读权限 255 积分 17604 相册 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 田老师国赛冲刺课
一:背景
* A4 A. l' l4 q! l7 F2 x 1. 讲故事
; h* `, j1 M3 X0 _4 ~5 D 最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
4 I5 ?' }5 i6 m) ?/ h " f; X! @, F, N: L) s1 A
二:了解架构图" F, w+ N+ F+ i M* G
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# ~. X. X1 a8 e* v( i% E' r- O
5 ]- r* {/ J, `# G3 i 1. 从架构图入手
- J, N% K2 c( s+ M* T8 { 大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。' d2 T5 ?. [6 U) o( m. P v) f
# z6 E- _6 p+ S2 A 4 f9 `( Y; d' |' Z+ c- }
0 W0 c$ c' K [( z8 D# s 其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
1 A" X r& C6 e0 k/ O/ L$ _" C) ^
0 }, G- R0 d" b% ?$ V @% D 2. 功能点介绍
9 O! W6 N+ v# O6 y MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。1 i, s. f% v ~( {* U
& r8 h/ C: ~$ Z6 _ <1> Client
# P+ m# ~7 r6 i$ a0 l# [6 s! i0 Q 不同语言的sdk遵守mysql协议就可以与mysqld进行互通。; A! {5 v2 e, n4 i; s% C
# }) S |/ A8 V <2> Connection/Thread Pool
0 V1 v% a4 U& ^" l MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。) x( F+ U; W. y. I6 ]5 s9 n1 [
5 @2 B& s; U# q
<3> SqlInterface,Parse,Optimizer,Cache
/ ]0 [- m/ F* A 对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。 Z9 m8 ~9 M+ O0 v8 s1 e L
" Q( M5 G) {: [: \( z! P <4> Storage Engines7 n& X. Z5 A& a. i6 w/ {3 S
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
: ~" }8 [* M0 P% P9 Z V4 }7 u/ Q5 e
三: 源码分析3 X7 @+ v: c6 H, [" z. R6 G% h, u0 A
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。& J" v1 ^" y) b( l( I
' ^4 W' N+ T J# Q
1. 了解mysql是如何启动监听的
) M' f+ {# }7 h; V7 M+ p: N 手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。3 c2 c$ t% X" ~
( z a0 x* i# q" [' _) E8 v5 H% w
2 Z2 D u, k. _+ i . [" \8 B! b# t- b) E
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。* Y& z6 l5 Q. |, K# W
: z9 s+ o. P' d
<1> mysqld_main 入口函数 => sql/main.cc
, K ?1 r3 l" t) t R * j& ~7 G5 k3 @- D% ]) E, r: A5 O0 H
8 c. a: w5 ~" Z extern int mysqld_main(int argc, char **argv); k B6 A" _2 q/ Q' f; x$ e. K- ^
# K' Y& D% R, N5 J/ \/ u5 T
int main(int argc, char **argv)
8 l6 o9 F2 C; a# u4 _. H {
; |+ i/ ?+ s4 N2 V6 Z return mysqld_main(argc, argv);
( Z( L! V4 ]' i. ]6 U3 I2 a4 p }9 L2 i0 {. h7 h6 O# b
3 _0 j# Z1 k& \1 w5 T2 w) b
) R1 X7 Q( v7 J& y) g. y 这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。& L5 k: ^3 D; h6 R( T/ X4 e
+ K$ D# u# p2 N
<2> 创建监听6 _2 x$ b- t- R% B
% m& ?) M9 [' P/ Q
9 M5 R, a0 D3 L$ A& W6 U
int mysqld_main(int argc, char **argv)
' c8 B# j: E E+ _' n5 E {1 u$ L+ Y. H, K9 _" C1 b
//创建服务监听线程& T# v' v6 ]7 J& K' Y4 G. m
handle_connections_sockets(); k4 t9 k2 U, J. [) A
}
8 L- `; _4 C2 T2 y3 F/ Z * ^1 Z5 v9 U1 ^) N5 w; L
void handle_connections_sockets()1 v1 e0 B y5 V
{& y) |9 p8 W' W0 g
//监听连接
7 v, ^# ^' _3 e6 R# ?. q' u new_sock= mysql_socket_accept(key_socket_client_connection, sock,0 K/ r: Q# j& h
(struct sockaddr *)(&cAddr), &length);8 [+ A. P" ^ I' ]3 j/ _
& G G0 ?. k* T4 W2 P2 _
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
2 ]" I! A$ `; L thd->security_ctx->set_host((char*) my_localhost);
0 T3 Z4 r7 `# j. g% M 0 T' X$ ~: _4 }
//创建连接
, U& o( X6 |! n4 B9 h. F8 r create_new_thread(thd);) U3 Y- v" @1 I1 }' O
}/ c r% t' G2 x1 i
1 @7 f' h- \% J& J8 d% V //创建新线程处理处理用户连接+ m i% I i! I F* ]/ z+ h
static void create_new_thread(THD *thd){4 j9 L1 i$ [. V& t' N0 D) V
5 Y! u7 q9 P8 p& Q thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
: _ }% y' u. ~; D) p $ |$ e6 ]$ f" m0 t
//线程进了线程调度器8 R8 T9 \1 P4 M6 c9 T/ i' P
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
1 U6 ?7 z* x6 q. C }% ]9 w* E* I' a# L7 E
4 s L5 x" m% s! K
; x8 ~ B7 ]7 V4 U 至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
" U9 I7 h Z- X8 a0 O, ^
: d2 {1 r/ B. j
& P9 Y& j. R; ~( V& x" a( f2 U 2. 理解mysql是如何处理sql请求! i/ M9 [5 Z; b. `' R1 z4 u* Z
这里我以Insert操作为例稍微解剖下处理流程:7 M3 b* Q5 k- t/ A, M: [
, }. U; N0 m8 H5 Q8 l. Y) |. d% \, } 当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。8 T# L3 `& u# Q9 Q2 u5 G: J7 d
$ {5 z5 ^$ I0 W- l" X* S1 r
& |- ] \, U7 E, l static scheduler_functions one_thread_per_connection_scheduler_functions= `+ r7 |, I" f0 S: S
{" C) i) H2 D; E$ k5 Q4 w
0, // max_threads
' e. b& N+ J! j- V V NULL, // init
4 `/ o6 E' \- p# _* z; n4 u init_new_connection_handler_thread, // init_new_connection_thread
7 P! O, O c/ s% ] W create_thread_to_handle_connection, // add_connection
. ~( a7 z& Z7 u+ r# F: O NULL, // thd_wait_begin
, B# P, M o5 j* s( u0 q. k. Z6 X0 u NULL, // thd_wait_end
, o" X2 ?% C4 k# K% p' Y NULL, // post_kill_notification0 \3 B. c0 b* F
one_thread_per_connection_end, // end_thread! T0 z& G( P+ E8 Q
NULL, // end) a" K3 h' E9 }- x# Z% a7 j
};% E, [, W/ [8 p2 Z7 [6 b& \
% j; G' D6 L/ C1 U9 k% H' N9 s* g p , R! `# r7 @6 C/ |
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。- }7 a6 u& ~1 [2 ]9 E: {& }2 P
, @; w6 X$ H* [0 z' w
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪" g. I, |& F) Y, e+ ?( X
; O- w+ O- s& r% C9 E* r4 E! S
void create_thread_to_handle_connection(THD *thd)
, S" P2 _8 s5 E6 Q6 o! `- R& V {
; r: c6 n$ X2 d; s* p0 S# _3 G if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
+ c! R+ z) \0 H& T) S* q handle_one_connection,(void*) thd))){}
. }& g$ o2 G# s: i+ I }
( s" b4 t& y) r# F: \3 S //触发回调函数 handle_one_connection( @3 ~) f: W3 L. H
pthread_handler_t handle_one_connection(void *arg)
6 B4 ]! m8 u: X4 X% g" I {" i" `2 p3 O, D- m, f: X
do_handle_one_connection(thd);; Y C* b& v4 e0 F& z% G
} q: v1 ~) J' ^/ m+ c+ G5 |
//继续处理; m6 j/ C! G8 g/ z% z Q
void do_handle_one_connection(THD *thd_arg){& \+ H6 f6 e; a' V! G
while (thd_is_connection_alive(thd))+ d$ w! [; W0 O6 U
{! l3 L3 D8 Z$ l* E$ C2 ^5 v+ o
mysql_audit_release(thd);. f/ S1 f8 N! \. z$ t
if (do_command(thd)) break; //这里的 do_command 继续处理
/ c3 _8 Y0 j: W( j) E }
, Y# W8 J* B7 c5 J# y }
& }. `+ A+ N; a- ?, ?7 H+ K //继续分发
: y* K9 N+ o3 M8 t' E8 y bool do_command(THD *thd)
8 X# V) x5 w3 v {
' G% M* @( u* h, E2 a T( @0 R return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));" \; e, Y4 a2 c( J+ A& D7 [
}
M+ r1 G3 h' p bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)- W* n1 |+ Y4 F: Q; ^
{
! o, r$ ^- R. y/ `6 X switch (command) {
& D- i% ~: a7 K4 c0 f! W case COM_INIT_DB: .... break;- `2 M3 M1 _! g0 b1 q* g) U* W
...1 b* K) c9 X: B/ ]2 D$ x5 o
case COM_QUERY: //查询语句: insert xxxx
* s; }! C& @ m" P& `% m% i mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
2 P8 U; K1 G" g2 t break;
U% J: g7 S. O4 |( v0 v i }# g* M9 L* `" ?- F2 @6 s; J* l7 u; ~
}. Q; N5 M& d+ F& T( b
//sql解析模块- b+ L& L( }8 G! a! \+ `
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
2 }' L5 B; K) v& T { C2 L0 q/ R* _ t+ f
error= mysql_execute_command(thd);/ f" _# b% P6 X" ]6 E7 }
}
# H: o$ f- U# g7 q' O$ M3 @ , M. F. {- [5 f' T
3 I% |) \( r3 H' v' t' ` <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。' O$ w2 Y7 f5 m) ~) P1 I2 z5 A
. I/ _7 z. M, G; d4 B, t1 G3 b9 J
//继续执行. B# s! W# n) {( h! Y6 u: p2 l
int mysql_execute_command(THD *thd)- @9 z$ J3 A% @8 R p- t
{5 ]3 {' u; C1 F$ y: X7 l; A. g9 ~8 Z
switch (lex->sql_command)
3 v2 c! s; o( S# i( @/ c' @ { |! S6 d5 D; x, Y t# b4 K7 a
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
2 S+ _% G6 q4 P; b9 g9 Y* h
- G( o5 K4 K, i8 k' O( b, R' g+ @ //这个 insert 就是我要追的
; _- Y( ]' t; ~* F# X case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
* t8 `$ Q& c7 S lex->update_list, lex->value_list,
, W: r8 f6 p b lex->duplicates, lex->ignore);
4 D. t+ m. u* w+ i6 \ }7 G6 x! l, M% ~* j J0 f w" e
}) {) e+ y+ E `) V3 S9 B/ x8 E
//insert插入操作处理
& V3 K" E0 ^: n bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
2 U; B" o. a2 t# O List<Item> &update_fields, List<Item> &update_values, 8 w) @7 C% D- p1 V C _
enum_duplicates duplic, bool ignore)1 l% z6 s" s' @
{
" F3 D' o t9 c( N3 B; P6 o while ((values= its++))
! a4 O* n5 M" x {5 p4 i1 ~8 X) R- ~9 b# z$ l! m6 l
error= write_record(thd, table, &info, &update);
, O- N8 Y8 ~5 B) p }
8 M+ h: _+ N; x6 `3 e5 k: Z' L }
( N( E9 ]& p% q: b2 O% X //写入记录
$ W% O5 \7 g7 l% d/ v: w% d* o# t int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)* @6 Z7 t5 `0 @ @5 J$ S
{
! K/ v( Q2 M4 T1 k% Z* Y+ \ if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
& [- S1 B/ x6 D: P {
- L# h, ^9 y) r% N& I // ha_write_row 重点是这个函数; s) B5 j3 F: I! i1 I: z6 L
while ((error=table->file->ha_write_row(table->record[0])))4 \9 {: D0 L0 W& k$ B) f0 ?
{1 i& q+ s: Y. o6 n6 K" Y
....
+ `) w9 T6 ]6 i }5 R( e! r) Z+ l- p$ T
}8 l5 L$ Q( M- A! }/ n
}) _3 F4 K% m5 W( c! a4 U. Z
, |2 m( b6 K, n- Z& |* v: z 9 ]4 Z% C/ n% k+ X: ~2 j, @
4 F. r5 s$ m0 c. H, Y) n 可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。& W& k. U- c6 \5 V$ m4 b
5 Z3 s8 I0 L6 A) p8 T
<3> 继续挖 ha_write_row
- Y! w' y/ C2 D& q
4 i+ \/ v; M8 q% j' }3 Y' ], I' o int handler::ha_write_row(uchar *buf)
- D# c) s; Y A3 E7 O( V/ C. ^+ r {
8 t% [+ X0 o; F# P/ x- h" J* m5 Q MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
: J" F. \# \8 D' x( ? }: I+ q9 d: k L/ J6 y
- {; |. C9 ?$ z; N, |" S
//这是一个虚方法7 V+ x/ R1 p- l4 d2 ^
virtual int write_row(uchar *buf __attribute__((unused)))6 X6 O* c8 i+ I7 K' \! l# F2 W
{
o- X D/ }$ { return HA_ERR_WRONG_COMMAND;
* \. E! G$ U; M+ o9 V }$ e- G. R+ M; U* Z4 o! ~
h. M. `! M8 }4 ^6 b8 r ) g# l' o9 q( r* ^) g
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁9 V- R6 B9 v8 m7 q2 [
! v6 o) n/ ]( ~9 H! Y
3. 调用链图' u0 q5 a" g. W1 h5 R7 v9 F
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。9 p) K: a2 ~$ g; H! B5 @, K
" }1 c: c7 P4 T& k' t
) k( F" n8 Z1 U% |: T; B: e
& g0 M2 d6 }+ Y- W" L. ?1 I- [ 三:总结8 Z. u& s' }9 g$ x6 V
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。 K9 p9 `( ~& }9 Q( K6 C
————————————————
" P$ C8 ^4 d5 p! i3 ^ 版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。% r9 {( c$ `( f! ^* X
原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874158 U) X8 I9 a; D8 c- H4 [
zan