- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55544 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17615
- 相册
- 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 田老师国赛冲刺课 |
一:背景- o9 W9 u% l$ ~' v# q( g
1. 讲故事
6 S3 K7 F* j4 O* ]# H5 o8 P9 S: F最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。- L; h- K" e$ f! ]/ u; E8 M0 z
& b) s3 P: m f* B$ I
二:了解架构图1 w0 `) x* w( k: `& C7 F
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
5 d7 J2 u! O8 |0 t& |- U! y( U- O) i+ q( N# l# N" o$ u
1. 从架构图入手
9 Q4 u# M9 c! }+ ?6 V5 u9 W大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
2 [% s$ W+ L5 u9 }: n% n7 Z. p+ {![]()
: d' y6 n" I/ ~. E
: Y; z. m1 C7 f1 _4 l0 g6 P
4 _ L6 Z, A+ f( D: T其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
: w7 b& e' A; }# R, c3 e
. s# Y; e7 v, u# q9 O {' A2. 功能点介绍
2 ~5 t8 K) ^( K8 u tMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
% L; K2 o7 S+ w$ E" t+ H( ~( ?
<1> Client
) A* `* J! T1 d( K不同语言的sdk遵守mysql协议就可以与mysqld进行互通。/ A8 @# Y& }( p8 E" M Q# U
) |; d3 i+ Q1 Z. x5 }/ {! M& u
<2> Connection/Thread Pool
3 X: R* ]; r; _5 A7 q \4 aMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。* c- z2 o# p, Q$ |5 q8 r3 k) p- A
3 v0 S3 u0 Y- P1 R6 |
<3> SqlInterface,Parse,Optimizer,Cache
2 e$ c2 l9 n$ K0 Y/ x- r对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。. H" D6 c7 m9 r4 T5 R
+ p' l3 X5 s+ I2 b' \ d<4> Storage Engines
3 K8 `; N2 ]* n) @负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。! C! K0 A' K S8 K2 _( u
% Q4 o8 j' ]& Y, L. X& i3 ?; D* A三: 源码分析
/ V; @0 `! \- Z: b- b: S关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
, N: K: N& q7 `# d
+ E* Q6 F1 }6 @+ F5 @1. 了解mysql是如何启动监听的( O, R; W7 ?1 s4 o7 @
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。1 J T; B3 l Q% V5 l
0 h9 c& U' W! d2 O: I; K, ~
: ` f9 W) `9 |8 S5 E2 F
, [! v; M, R6 z* n+ u
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
: n& v+ K) G2 Q# w+ h
' i. N/ P9 _, J' \/ A% T6 Z<1> mysqld_main 入口函数 => sql/main.cc3 H1 n' i1 t4 ]
( L- x: I( R% w: e6 N
, n! A: d. f9 K3 B3 _4 A
extern int mysqld_main(int argc, char **argv);9 P* s+ W+ M7 u( y
# F- A& U4 B4 d: B6 ]; c/ n& nint main(int argc, char **argv)
0 ~5 Y* G3 ^0 g" ~" N{
8 _% l* y/ ~ Z' _5 r" }' h3 y* o/ M return mysqld_main(argc, argv);
8 w4 F! S8 T0 C1 l}
- a x( e5 h4 {; |
6 F* a A, M$ H5 ]5 O8 l. R) W
; f. y8 L7 G0 y5 }4 a( O" g这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。) u( K: ]* _7 s9 N* m/ [4 N
' P" `" z( i2 K& C
<2> 创建监听+ Y2 h% S$ x: [" C: [
$ @3 @- r: L' c
$ B) F& D0 {+ T# @2 _* p3 |6 M- eint mysqld_main(int argc, char **argv)
% C: a( q8 M& t% E. d( @6 t: |{
: K# z( v9 r; ], i2 M //创建服务监听线程
% ?- f" M y* S handle_connections_sockets();
" u* z2 X4 v3 V9 s# R6 E) D}5 h+ n( ?1 g( m
4 _$ q: z" M& d' dvoid handle_connections_sockets()
- M& T0 y7 T; B{0 _0 b2 [8 ~! m0 j" v( B2 P: b
//监听连接
9 X' ?1 h' G# } new_sock= mysql_socket_accept(key_socket_client_connection, sock,
5 H8 R# V4 @5 z/ k+ H (struct sockaddr *)(&cAddr), &length);# A5 b% O5 h- [) O
; Z! K: B& I( J if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))2 ~$ @1 P4 n8 j
thd->security_ctx->set_host((char*) my_localhost); G* W- r; Z& E" P
. w4 S6 `/ A7 [3 p$ ?
//创建连接- }4 b: {' B3 s$ D
create_new_thread(thd);; y) c0 p$ J: d' d
}
( \# i) H$ r9 e8 E8 q
h" H; O' P8 B0 g) c//创建新线程处理处理用户连接) h$ e( F8 J/ R" w; Y( W8 `+ ]$ U
static void create_new_thread(THD *thd){ r8 m+ k! Z- w1 ?3 A% d4 u
( p: e7 L$ j7 O( S$ j5 P% S& s1 a# F thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;0 \1 T3 Y; R) ^; i' u% k: o
1 ~- _6 Y% `9 j# I
//线程进了线程调度器
4 F9 K1 ]. n2 m$ a- C( O( ^- X MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); ) B% G: f$ a* K3 H1 d
}
" U/ }; H n: T+ i% W7 F2 V
, U( |6 k* |4 Z( j, _8 }( l
) J/ ^. P& Y3 D2 O至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
3 a0 a8 h3 V3 j5 ?2 c f
1 Z# S" N: E/ Q* g. d8 F1 v# ~, ^; h
2. 理解mysql是如何处理sql请求
% n8 r4 Z& M) o& H" b7 v. {这里我以Insert操作为例稍微解剖下处理流程:/ C6 }: S1 k* n0 h
2 H; r, V% l/ D1 S8 l
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。( W4 n1 g$ O* M5 x# y4 d3 }& ]& |
' V- b7 a8 @, E, J
6 \; c6 s% a: }/ lstatic scheduler_functions one_thread_per_connection_scheduler_functions=
3 o5 x0 n: @0 Y1 h) ^, L{' O* j9 h& W6 A' r; d. P/ Y
0, // max_threads
/ @ {( P6 J, P& s6 Z. c NULL, // init
. |4 x" v. U$ w0 K9 D init_new_connection_handler_thread, // init_new_connection_thread+ i }/ S! g" g, `7 \. `! _) V
create_thread_to_handle_connection, // add_connection2 W& q: s. G: l' T8 f
NULL, // thd_wait_begin0 v7 |; K+ q8 ?
NULL, // thd_wait_end
9 Q7 U" U( o+ F) |" G% K NULL, // post_kill_notification
1 K8 x( I6 j0 c one_thread_per_connection_end, // end_thread
# B$ I6 v# I; C h! A; d8 X NULL, // end# _$ A. \2 M6 ]/ i0 ]. s# N* ]
};
( T8 z( g5 _) z" H# X D% b& r' x- I% x/ S
0 R( R$ M7 N% a" {- r2 M3 P# L从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。0 S' c; K% A2 W
0 Q8 {' Y4 q- z$ D9 L6 u* P& j1 ^
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
+ l" Y) t' v* Q5 g3 c0 R/ H5 r I+ X l n3 h! k. l8 K% b! Z
void create_thread_to_handle_connection(THD *thd); G' z, ?: E8 E6 V8 N! h
{# |' _/ H" T5 o2 U% S) u
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
+ c* O- R# C' t/ q3 d q9 i handle_one_connection,(void*) thd))){}
% C: l$ G4 ?8 R8 s9 Q2 V' O5 T: z* z6 @! b}+ q7 n' ^$ o+ j1 Q7 K' [
//触发回调函数 handle_one_connection
9 F& o1 c- A2 n8 i1 `# wpthread_handler_t handle_one_connection(void *arg)
9 i/ f5 p C. u{# [/ }2 R0 `/ P+ Z w
do_handle_one_connection(thd);
3 e6 a: [# W) k( U}+ P0 d7 K* a% v# i& t
//继续处理
- P; H! \/ T7 b: s& Z) x' ]void do_handle_one_connection(THD *thd_arg){/ M: m0 x7 W: T
while (thd_is_connection_alive(thd)), @9 L2 ]% ~' ^$ w) Z Q
{
7 U4 Z! ^4 J$ B1 M mysql_audit_release(thd);
% D- R& M$ e2 r$ c if (do_command(thd)) break; //这里的 do_command 继续处理
% \% O' d" A; }1 ` }# ?( @* p5 x+ q, ^1 u2 ^
}
% I/ I" T% e) a0 j6 G3 k. R//继续分发
8 d0 F4 V& Z- i5 W% @bool do_command(THD *thd)9 Z7 h5 a$ u% N% | W+ A3 Q. `
{' z1 e' o! y( N8 k) f2 L O
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
, Y! e. B! z4 ]# x' A3 k}
: ~' Y/ U, l1 Y) sbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)2 |) x% J$ {8 Z4 m( d
{
$ L2 j, e4 R' d( b4 E3 V- I switch (command) {
/ z/ [; N$ j; W; ^ case COM_INIT_DB: .... break;( \ `" a5 I* @% ^4 G! [' Q" P! c
...( J/ D3 J& C, L$ o
case COM_QUERY: //查询语句: insert xxxx
; j* }! Q& P# y2 Y- ]. z mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析9 X+ b4 z: w) E4 o( ^8 A
break;
' t1 Y* L( G6 A/ F }% g+ e) Q/ ?" I$ P$ t5 M# ?7 ^
}
' q1 c( W8 j3 N# a//sql解析模块
# h: {& h, J" A2 P! w: E7 [6 @void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state). @. O: X' \/ ~9 v8 F
{1 w4 }4 B3 G9 g1 t3 q3 s. \
error= mysql_execute_command(thd);
* h) O" h7 P: F# f}3 T7 z2 u* C, M& k
" c5 Z( @( k+ W# J# d# ?$ b3 T/ U; S2 ^* `4 s {3 o# G& S
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。+ H8 T P0 \& `7 B
1 O4 d2 ~ x4 k9 Q1 @' U
//继续执行
0 q! ~* @2 |# W. Q2 {7 q# U8 Cint mysql_execute_command(THD *thd)
m" }1 n3 A6 h8 }, P2 ^! E{ {; a4 Y" m+ L2 v h7 c5 D
switch (lex->sql_command)
% J. ^8 I* L* z! A1 B. V# y {5 y Z* z- ?2 N5 t
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
& g4 r- C: M9 k+ i: [% ~# g. B7 c0 F
//这个 insert 就是我要追的$ Y1 J l( e+ C' e( P/ U
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
) ^& {! Y8 C! }2 |1 f1 E7 A lex->update_list, lex->value_list,. y3 a5 g% t% R8 d! D
lex->duplicates, lex->ignore);
/ t( ^) V: A; K- c& P } T) S- Q( ?" q h
}
, e0 Y" V" I6 z//insert插入操作处理
6 S4 h9 y% m* j0 ^7 {: t) ibool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
1 b/ B' I" B) U* j0 R: t0 ` List<Item> &update_fields, List<Item> &update_values,
9 c, E$ ~) F5 ~ enum_duplicates duplic, bool ignore)
$ O: C0 s& X6 e, Z8 k+ O{, X4 v+ f" Y4 u/ |$ l2 v; s
while ((values= its++))
( u N- ^* D9 x3 x* _9 ^& D! M {
6 y3 X8 F/ a* X6 P/ j0 P& ? error= write_record(thd, table, &info, &update);
2 O; J' v" N+ F) Z6 x }5 C- \9 @0 t# L/ P3 `$ |: t8 b9 |
}
7 e; g# x$ X X- A% k2 B//写入记录" r$ F' t1 c% H6 L$ o/ M# e
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
% Z& x4 o5 t L& V4 s{
( u( x3 H# |7 V6 i6 s8 L if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
3 G4 e: f/ ^4 x {
9 T, l( A& k% O // ha_write_row 重点是这个函数
5 N/ ~8 V# ?5 D8 c S7 c4 C while ((error=table->file->ha_write_row(table->record[0])))
4 k( O8 u" _ Y+ c3 R- l4 s2 I {
9 s3 S, v; U( f6 t+ F- e ....$ v# L* O- E1 s# m/ B- }4 C: j
}3 S( T i7 r' E% ^/ g% {( ~3 W
}
+ `; F- c% u8 R( ]* e, Q* P( g9 s}0 ^* D% [ g6 e# @/ u0 @
$ w' x& B9 f4 G3 j
# E7 Y! b. \: c
. s- }. v3 c' X" @+ O可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
: Y+ W9 E) v) ^6 J% [
1 ^) S) T/ [% G3 I9 ?0 t<3> 继续挖 ha_write_row5 X8 V$ w8 W" d
# ]& T4 ^2 _. kint handler::ha_write_row(uchar *buf)
( ]/ @4 A4 ?" k1 `7 M{! H8 P/ B8 |# f" f2 C
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })+ C5 M4 q0 a9 Y; Z5 O* @
}
: g+ ?2 A, C) Y1 A9 y% Z! W7 y4 h) F( F. Q8 H0 y
//这是一个虚方法
$ H: ?) o" \; O& qvirtual int write_row(uchar *buf __attribute__((unused)))
( J8 H" T; G/ m, T8 s3 V{" ?3 ~, F, [1 ~* \/ e
return HA_ERR_WRONG_COMMAND;
9 T) ?) B$ y' l4 N1 `. p9 k} b; a; i. X- g
% `5 V8 C; n. ~8 m: d, P( m9 {% \) X! A% n: X7 C# S1 W, H7 p
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁2 @2 |( k% B- E* `
3 Q9 W. j& k* [0 p: j9 r, l- |3. 调用链图9 g, R% }! A& b0 h5 O0 A) `! W
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
2 p9 I t$ p; _$ H' N0 m& _ ! ?* x6 K7 t' G
# _, x# J. h( P
" B+ {3 b# g* H5 B6 [6 g三:总结
, C5 O3 o4 W" S* o7 b大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。0 O( p( U3 U$ h
————————————————% y; ?3 A/ m9 k' l- ^
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
+ m$ h+ d+ A6 e7 i原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
& g1 @- ]; l! I+ o3 x% o |
zan
|