- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55508 点
- 威望
- 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 田老师国赛冲刺课 |
一:背景
2 ?7 I7 k+ ~ K# X1. 讲故事0 ]- Q) t6 I# U4 T; m/ R- @% S: R8 p
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
i' h2 Q+ O% r' F8 A0 H9 C
! z; D" [" ]# Z: i0 X/ k' h二:了解架构图
! \" n% D ?, C. Z& k& F! g. Rmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# n k0 R) a0 X& @# V2 H8 w- G# j5 s
# H" w) `6 @) t0 b. S5 Z
1. 从架构图入手$ A0 w6 U& b' n, n
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
4 Y' D; A! p+ I7 f+ v$ O![]()
8 ]3 C T5 d5 n- y, F& d2 z8 H9 e+ s: R9 L9 d% O0 H. c
7 [7 |( a# B; G: r* I其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
4 o# k7 V8 J! p( O0 E
2 ?( [; f" B0 \' ^/ l) `# P; [; w0 [$ t2. 功能点介绍* h% e0 o4 G) H5 M
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
& q+ }. x# Y( }" P0 ]4 o4 e8 ~# `; r; T5 p+ ~9 Z- N3 r2 ^
<1> Client- J) N! U4 Z) J4 H* ]
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。2 J! E( E( |; d; s/ S
3 \: W. x, V9 a. L9 S<2> Connection/Thread Pool
9 N7 u9 n2 ?# h6 dMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。1 x2 F) P( _3 ?- A% N* ^9 Z& Y
- `( p/ R% T# y: v& }0 F+ ]
<3> SqlInterface,Parse,Optimizer,Cache/ }% `1 k) K9 r& r
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
9 `/ A5 G, ?0 |# B1 v5 l- ~% v; w( C7 b$ r2 X0 y( H, k
<4> Storage Engines+ K, M" v& D2 j" \0 g# }# |
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
) j5 q( R! }# |1 R! l: q& `
( M& }9 y7 U! r( ~" ^三: 源码分析
' _$ Z; Z7 f! J# E关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
3 i" |" M" [3 q3 {8 l- G% v6 Q) I9 X t3 m' z
1. 了解mysql是如何启动监听的& ?7 r; L. Y; v% x" _
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。0 t8 o6 o$ C1 t( `# T% X3 ?
![]()
2 v, N4 e0 b: Q+ o$ m7 E6 ^. p" j
3 B: j7 g. _. U7 B; g从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
6 s) N/ [) H/ ] d# o* t
2 x! F: X* f* a2 z8 y t<1> mysqld_main 入口函数 => sql/main.cc
. D* x: U+ r3 H+ n+ h) }- t1 W: J& |; e. F( r- Q
+ |' o- s( n7 V- fextern int mysqld_main(int argc, char **argv);$ _. D) q1 S5 G* X6 ~0 i, M+ y% k! j
( A: ^* w9 S5 g0 ?! _+ T% b0 Cint main(int argc, char **argv)
- m {3 g! T$ s4 H" E; f: C- q( E{
! G X8 w+ S3 r _* B return mysqld_main(argc, argv);
! _+ G0 T: ?8 B- x x i}# s. k( C) k. k/ ], u& l& N# J
* }6 e+ ?8 j) c& y1 ~) L. }
$ J* s5 m9 _& Y* S( M这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
. t+ y6 Y/ v& z( K7 I% {) u# V! Q9 U N+ ]3 F# A
<2> 创建监听& q) a4 k: Q5 E
5 Q! A7 W! r+ o y8 I
8 c! x; G8 t# U/ ~, ]int mysqld_main(int argc, char **argv)
. m# f: ^- ^/ T{8 A& f C- k; U* b1 T
//创建服务监听线程
8 E A$ A* a5 v handle_connections_sockets();7 \) u8 x. {4 \
}4 w2 @8 n$ p% n# o: }7 `
# w! G0 X2 x" Y# f" x0 L; T4 _2 r$ R
void handle_connections_sockets()
2 ]9 Y1 A4 P/ J& N7 N{ R- X. P5 {5 r" N' v8 `$ l* w' e
//监听连接6 r7 R5 t, M9 |7 W# t: p' {
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
+ v# {: x Q/ Z2 x+ S6 c! B$ u# Z (struct sockaddr *)(&cAddr), &length);- n: D: j! X% e5 m
9 z; F/ S% R8 h if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
: x/ E) a! ~+ F8 Y2 H: O% \* \, A thd->security_ctx->set_host((char*) my_localhost);/ u! i# c- O; w! I) \
- ?% ]7 ?/ j+ L2 z/ }" \0 u
//创建连接$ h4 d5 X2 w f" z. S, W9 o
create_new_thread(thd);/ L2 J' D0 M7 x3 [. y
}: ]; }- } b9 D3 m5 Z
1 |& j5 {3 l# u' b0 ?' w( Y, R$ L
//创建新线程处理处理用户连接6 N* M2 A, e& u5 K# d Z8 Z4 R' c- H: |
static void create_new_thread(THD *thd){
7 g! y* B' l- w) V3 f9 \4 i+ \ }" F; W/ J% o1 x; P0 r& E* L
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;6 V/ _% E3 W, s. y3 I" T8 n
# ~0 _8 V! s4 t" h" O: O //线程进了线程调度器
1 C B! d+ J+ y( c, o. L; E: P MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
/ h9 B% Z. w7 k2 T+ a( I}
- Q# R( D8 A+ e5 x; G8 Y% o8 c$ d/ M* A( m' J$ M, P [, U7 q
# }. }5 \1 A5 |6 N0 G1 @
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。9 A3 f& Q3 R6 t( n! I
& K6 E* q7 K5 D C% R0 _# R
3 h3 Z4 L/ u/ ?& O
2. 理解mysql是如何处理sql请求
! w0 N4 r i8 q+ D0 j这里我以Insert操作为例稍微解剖下处理流程:
# q% H0 r9 y- m" u) D5 b6 @
$ R: N: i7 ?% l& V8 ^当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
( ~& j5 J$ S7 g# o1 R8 V: b7 l
% d" \4 D- f8 \
) x/ ?5 d( P) } F3 r: ostatic scheduler_functions one_thread_per_connection_scheduler_functions=
- x) F ]0 y: K% j" s( ]% G{5 @* q4 h+ Q6 k% V' ^
0, // max_threads
' Z, t6 }. `. n. F3 ^# _- f NULL, // init
5 w- }. m$ U( U init_new_connection_handler_thread, // init_new_connection_thread
9 G+ |1 r/ l1 |$ z create_thread_to_handle_connection, // add_connection9 Y" u5 l/ X2 B9 ?( E% U
NULL, // thd_wait_begin
' l$ g5 J3 Z, o/ ]/ H& y NULL, // thd_wait_end: {* F4 K6 U- Q# X" A
NULL, // post_kill_notification# `% h/ I, J& a0 O3 r
one_thread_per_connection_end, // end_thread# ]. }/ n7 u2 e2 @2 }
NULL, // end
7 ]2 x! f. a/ y+ A};' `% v# q# m, h
N* z8 I! E* u6 M* ~' X3 C/ j* `5 ]- r6 L
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。. i* i6 }$ |+ f; Y! }
# z4 P: a7 Y; \- ?- J<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪: S9 ^1 ` K- R7 N% P
3 p: F8 S; O0 J& k! F
void create_thread_to_handle_connection(THD *thd)
3 G. A% b/ G) B{5 M1 t* Z; O' w% ?9 |
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
7 z* a3 u0 k: T; _/ b handle_one_connection,(void*) thd))){}
2 n# |: b# s% a0 Z! G3 i' j}0 |3 H1 v6 Q0 a% n0 i# _' h
//触发回调函数 handle_one_connection
5 {( Z6 M& j$ W7 [6 rpthread_handler_t handle_one_connection(void *arg)7 ~7 \. F3 E/ ]* T# E) u
{
: c$ G* K8 ]8 u do_handle_one_connection(thd);( s' P8 r! O: j! R0 H: T3 f
}# k% |+ T8 v" j$ a7 k# K7 W0 p- F
//继续处理# N* d$ `2 C5 ]: c
void do_handle_one_connection(THD *thd_arg){5 _2 y( p5 c4 R4 A
while (thd_is_connection_alive(thd))
$ m: u T% [: D; k, k% D6 M {
- t4 d) |, W* n) p1 y+ i* x9 N mysql_audit_release(thd);
{2 p7 t1 p7 c" h. N. i/ B if (do_command(thd)) break; //这里的 do_command 继续处理
% {6 k8 D2 e) h: A2 K }/ m; n& s' o( n8 A( E
}
1 C9 [+ Q) n$ a$ u, z//继续分发
" L2 s; _* l7 Cbool do_command(THD *thd)
+ M& B! O+ R5 k v0 I: F3 |{2 C9 v$ V1 W C( V
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
$ z- |) V1 R h! u& O% j+ P+ O}
$ R0 o. R0 u$ B1 F- O. s. N/ U3 h% `bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)* C( ~+ r" q1 @3 V/ d" k$ g
{
2 h) e; ]* Q' R6 R( y: e switch (command) {, B1 ^6 O: O8 q$ L# e8 [
case COM_INIT_DB: .... break;
- }7 x. @1 y! n4 U ...+ s' e, ^7 c" w' `7 c! j2 c+ _
case COM_QUERY: //查询语句: insert xxxx/ |& x0 b, |* I9 V" R
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
" d: I9 ?& Y3 K% D$ G1 M# w! { break;
$ w, k6 n# i* ^' f }
" u2 {9 Q. v/ \; k% |+ I, M! n# \2 `" @}
$ U$ H' [4 G' U2 V3 k$ m( N//sql解析模块; i7 [0 Q" A- n$ H( e
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
7 O5 a4 X+ X9 |{6 Q# d2 Z4 v5 P* N0 b; z
error= mysql_execute_command(thd);
( r( r% ]5 {/ |3 E8 }}' T$ g# c X3 C& w
& V3 k$ Y- Q% l$ t1 ^" l& x2 ~1 L& L7 G5 f3 b+ [
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。+ D% ~( ?# l: S
W- z) M) g0 o" y
//继续执行7 H, {* l7 F! V" n- S V
int mysql_execute_command(THD *thd); V% U+ a2 d& }- d
{
( o6 E L( Y0 X3 s% t7 } switch (lex->sql_command) " l/ j j/ V/ r- B7 W2 z
{
" ~% X2 g" N/ {0 Q8 Y case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
6 b/ f; K2 S; Q( x' l* p" s9 r* f3 q# n7 r& ?
//这个 insert 就是我要追的
# [8 q4 G P) v/ n7 m. Y* [ case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
/ U. c2 q( K( r% T lex->update_list, lex->value_list,
# r x& s% H+ k lex->duplicates, lex->ignore);" ]' y) a v) q7 T4 h9 f6 @
}5 `4 y R; s3 F) g
}
! V% W/ e" W1 ?4 C, D7 f+ ~6 k//insert插入操作处理8 O+ T5 g/ \7 ` _
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
5 L+ X% P; u! ~0 \( j) c4 ? List<Item> &update_fields, List<Item> &update_values, , t- F0 k! A8 A9 I' l
enum_duplicates duplic, bool ignore)
/ k1 E: ?6 `; f( @ D{
2 g7 b. Z7 t/ f) c1 [ while ((values= its++))
* E# G/ h& x3 K& o1 v9 I {2 p) l: s5 G7 G/ L0 q; N+ t i
error= write_record(thd, table, &info, &update);
4 ^5 f8 Z, m0 ~( K9 k }* ]% z. f3 w/ ? D7 y. q
}
& u( z2 w5 r6 p: z1 B//写入记录
9 b# Q& h) F. {2 |5 }, b8 S3 }int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
! ~/ J. r2 V# a' }# N{; X# T) T% H2 Y3 t1 x }1 [" I
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
& W7 B9 @7 u: V U$ t9 Q2 |$ a {+ d( C2 p2 P1 {5 n* z) D/ |/ i [- S! P
// ha_write_row 重点是这个函数* ~- u% @5 |& K, H: ?7 Y1 G6 o
while ((error=table->file->ha_write_row(table->record[0])))
Z b' i$ Y9 T/ |5 }5 \ {
8 h: f) X. J d; o ....; ]4 N% }* j G$ S( H5 I8 d
}2 l; L0 ?4 w4 U+ o0 k! K* K$ W
}
. b" {9 o. g- a! A6 b}; T$ Q* G, _2 O( q
& L5 v L; b0 e3 Y1 @
5 T; S# W+ c5 I. r4 S1 L! R7 X
2 j7 q8 ~; s6 @% Y. p/ d8 p# L可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。6 w# d; [, ]/ \" r; E! \6 j
' E r% O J3 n3 I* ?! W9 r<3> 继续挖 ha_write_row" n# D2 m* g8 S$ [7 |" a
- ^* U6 o4 a3 P R/ ]: H/ J
int handler::ha_write_row(uchar *buf)
1 M$ {/ y# n& t* V{3 K8 Q6 r; D6 n# D) e
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })$ Z8 R o f2 f S/ ]
}4 [1 `8 ?" N- j0 P* V1 z h( L4 b' K
" R# J5 n. d, f0 R6 w( X/ g5 |, m//这是一个虚方法' l1 N( j( n# ?7 T7 _- h
virtual int write_row(uchar *buf __attribute__((unused)))
! F1 }2 X$ G1 S7 r: ?2 L4 M{
! e3 e6 j- D5 p# _0 u: c return HA_ERR_WRONG_COMMAND;
. C t% K2 |1 v. t. e, B}
2 J2 m8 c2 t6 B0 k X8 [. o
3 J8 M! j9 h1 k3 j) `9 _9 H" @& z7 D% L: D9 d$ l
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
( E- z0 i; D) k$ V$ ~1 h' L
: }5 I2 Q7 F( h; o3. 调用链图
- _. @7 Q) R9 }! W2 F5 k这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
- b8 r3 }1 M6 D h! b/ ? 0 O6 w% t4 C" m6 v, h
+ F6 g& A5 t2 s3 T- r
+ R/ M% z7 \( b7 u7 W三:总结# Z. k' L: J8 X" }
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。; ?$ |$ W; b7 T( P
————————————————
) w9 S- e; C# `: y( G2 U版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。+ t k4 w9 B0 }
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
# `; z( J# H. D8 o3 T q7 W |
zan
|