- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55541 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17614
- 相册
- 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 田老师国赛冲刺课 |
一:背景6 b! p1 t. }8 Q/ z7 e: u8 S
1. 讲故事& m1 U D2 T2 }' h% Z, o! i2 Q6 s
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
. ]" o( v! n# m; U+ Y7 J) g W* ^8 I. X# d
二:了解架构图5 J: t9 J/ s7 x; I$ G
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。9 E: _0 G7 T: n2 i1 ~ @ ^2 T8 ^+ d
# W" | l4 C2 ]# g1. 从架构图入手! P$ C7 V" X; w7 }5 N, W" R* {9 `
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
9 B, D5 r; S9 u* o, S1 d![]()
, @7 t8 i* Z3 P$ t4 D; L" ?: A" |
6 \6 V; [$ c; U9 G0 N2 n其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
" Y2 Q4 l+ W# w c9 E( P: U& Y
1 e" J& M% a! N: m2. 功能点介绍
$ ~ d( I9 C8 n1 A8 p9 i" ]MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。# D& h1 N6 ]6 r
1 }% k1 n& P" N' ^5 H# k
<1> Client
" Z! X6 y. a0 Y0 A! _) e1 R( l不同语言的sdk遵守mysql协议就可以与mysqld进行互通。0 L% J- h4 ]& i& E; T
" `% @9 C# N% D) m/ z- [& k, T
<2> Connection/Thread Pool" ~7 p6 Y' G2 c; o4 _' f8 {
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。) T% L$ b5 ]/ I2 x5 j! w; g7 F
% k* P& B# e( w" H. c; p) u- G
<3> SqlInterface,Parse,Optimizer,Cache
s& S# F8 v' t; A, U3 D. e4 w对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
/ U# t' p, t( i4 t$ W
: C# P, a1 C' i; ^, J<4> Storage Engines
& p8 c3 O4 e2 F1 G- q负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。# P: P/ B d+ p% ^
: m0 F% a- p: f0 q- o9 t% ]" s三: 源码分析
0 ]2 S2 A3 w' m+ }8 e2 Y9 p+ S关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
P5 d3 [# w1 q. n9 x- M) W9 D
7 d* j- a+ e' G, A1. 了解mysql是如何启动监听的
# y! s9 k$ L! r# v手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
( w7 P7 A5 v7 D2 u0 {. [( |, b![]()
2 l2 ]: ~1 V# e* V( n! @$ c" ^9 L. ~$ A
8 }# e4 w* p% o! ^0 F8 N从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
. u1 [$ Z% H& A( A$ r0 _4 G* c* N: y$ I1 u! ~( T' l) g: `6 y
<1> mysqld_main 入口函数 => sql/main.cc( T3 w/ U9 L: Z& F1 t4 u& l3 L( B
3 p |7 Q0 p3 q- b, I) w7 c/ _6 V0 o0 [- u% q* \
extern int mysqld_main(int argc, char **argv);- T N1 @ O' \
& e2 w3 h. A. \1 S
int main(int argc, char **argv)6 g. C- G+ ^! O% c4 \
{4 P5 ?; T+ d0 e8 r& T% U
return mysqld_main(argc, argv);
2 t& d7 [+ B( H, N; a0 W( B}
( g+ G' e6 W$ [2 [0 S3 o' u1 g p: W9 |# k' g3 |+ Z
9 B( b( _' l5 V5 {; M5 k. g这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。# b1 M& l+ d" i: ~
8 y4 R0 T4 n; m: e% a: t
<2> 创建监听; S4 L/ n& F, C9 c8 ]& v2 i
% J9 s7 v" B- k/ g) @
! u) \4 ^8 {- Z; fint mysqld_main(int argc, char **argv)' ~1 {+ {5 K: P' J6 v/ ^5 k
{
( s5 a$ @& I( V m0 O- Q7 G$ N //创建服务监听线程
+ C' i; o) o: |! `/ ]: d$ j handle_connections_sockets();
. o) k, J/ H E}' t7 o- I0 J) ~, {* T* C1 O
6 ?5 B: t }7 A; R0 {
void handle_connections_sockets()
1 N3 ^1 J' g! S$ O7 d K. T }{' ]9 }$ g- H& m5 R% M( w/ y
//监听连接, y, s& ^9 e1 \& k
new_sock= mysql_socket_accept(key_socket_client_connection, sock," _9 R3 a( l6 K7 d
(struct sockaddr *)(&cAddr), &length);
+ Z y8 ~# [* M1 C$ K
/ p3 ~1 D* C" V2 l3 ` if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))" }3 X& G: y( ~
thd->security_ctx->set_host((char*) my_localhost); G% g) P! s3 A5 Q
_9 [5 _3 a) N% H
//创建连接
& \2 N6 q9 e6 l- ^: ?4 I8 ~ create_new_thread(thd);6 L9 {7 v w6 x! Y [
}
* c) z4 G+ K1 D) ]/ o; D( Q9 p" U4 Z% R
//创建新线程处理处理用户连接
0 ^2 X) F+ F; i$ I# R* ]% l" o x5 \static void create_new_thread(THD *thd){
9 V$ l B3 `) @! j4 F8 E& B( [0 R, n& Y/ \) Z. F2 B
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
2 ]# e3 m [+ s: ?% S
4 z; f8 L- `3 R1 a //线程进了线程调度器
2 m1 S6 r; I2 I: H( t" Z7 W9 h MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); j+ l7 u% j% R4 z. f
}
/ ?' ~& E/ q. X1 r5 M! ^
/ i# C6 g4 F! k& W+ b7 y0 D- A# n7 M, {% F) F4 n; U- L
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。8 R) b4 ^9 ?; i9 q
" E: Q: o& n: Z" D; f6 T+ c7 X7 |# c/ l: e% U8 H
2. 理解mysql是如何处理sql请求2 [3 b; f+ X6 K, f& B
这里我以Insert操作为例稍微解剖下处理流程:
) ~: o h6 J b) S! R" ^6 F4 W: h3 H, y: w. w9 a& l, Y. X6 d4 E
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。1 B1 ^# [4 M; a4 ~$ L
' o# i1 U0 {3 d" ]* o9 e& e
) O1 u L7 M! G o' A
static scheduler_functions one_thread_per_connection_scheduler_functions=
4 A6 m: n. Q# Q/ d{$ o% D6 D5 }# `9 g$ H
0, // max_threads
' Z1 H1 `1 q% y( ^% ] NULL, // init, ?5 B: ^: h V+ b; I( L- U$ W& B6 D5 J
init_new_connection_handler_thread, // init_new_connection_thread+ h' q9 v0 m: A4 O* ^+ X+ D
create_thread_to_handle_connection, // add_connection& Z8 O0 L4 B' x6 U- V; U5 K \
NULL, // thd_wait_begin6 X% y: J6 l, a" H" C: z' q
NULL, // thd_wait_end
0 R& t% I, u* }9 a NULL, // post_kill_notification
- s) \" r7 M8 z; \ one_thread_per_connection_end, // end_thread
! Y/ t0 i d m: W% w9 @% y NULL, // end
) F! q5 d& B+ D; ?};2 m& {' v3 O/ Y: b) T* h2 U
2 I3 s+ w4 q, \2 J
' b+ k8 ?9 C/ L2 ^" i7 Q" S
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
; z8 Z5 B3 ^# w( s! Q, N! K
1 J8 h6 Z* ]6 ~4 S2 R$ y/ p1 Y<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
o. m' u6 d4 M* x" F, w: m! {4 F4 L! |, ?) v
void create_thread_to_handle_connection(THD *thd)
/ q- ~2 U" |8 }# K. h: b2 x{& c: e9 X4 Q: @; D1 b T, z0 t
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,! S% l( ~% k& ~1 ]4 c
handle_one_connection,(void*) thd))){}
; k5 V! i3 r3 O$ V} A: a# m3 g5 j5 F& n; l/ |
//触发回调函数 handle_one_connection
% Q$ w5 B5 Y' O; M/ ?! m$ Gpthread_handler_t handle_one_connection(void *arg)8 s/ [' W5 Q" P, j$ O t2 F" f* D c
{
- @4 Q7 H' D1 `$ S- m$ @+ [ z( F9 j do_handle_one_connection(thd);
7 B( O4 K! q7 d% c}! }3 n2 q- H, ?, w! R1 o$ k5 }
//继续处理
& I0 d" k( O% E" zvoid do_handle_one_connection(THD *thd_arg){. p% _ g/ x7 m$ _2 e! [
while (thd_is_connection_alive(thd))0 V" |5 y7 M0 I4 m2 q& [% F
{8 I1 ^ w( F/ b: M1 P
mysql_audit_release(thd);
0 R" B. i) t1 J( {8 M. X- I if (do_command(thd)) break; //这里的 do_command 继续处理8 H) L& K) w# Z0 A- C- J- q
}
! ?+ W! z: y" i9 g0 }}
% O4 B3 a9 ]! W: C" P% H9 m//继续分发
* t- U% L: m) T1 `3 f2 U; ]. W' Fbool do_command(THD *thd)/ \ t% Z9 I% [# b9 D( ^
{; X2 W) \8 t0 M' O: W, P9 D
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
+ G! [. a- [/ o# r* Y}
2 N" D' C; l& I& M( t4 z5 B3 o2 {bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)' [. N; ^% {) w! h/ P% V' u2 R5 o
{
8 e4 a, V* U; Z3 X3 U* s& Q switch (command) {/ ?5 K) F9 w H# s3 f% j- M% E
case COM_INIT_DB: .... break;! p5 o2 E0 e3 Y
...) `1 ?, c. |) c/ m+ `8 b6 j
case COM_QUERY: //查询语句: insert xxxx7 _3 q8 j3 v1 c
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析' u' P! ^! X/ t3 v/ `- L& a# M
break;
. m% ~1 S! ]1 D0 \0 B }
5 X7 y+ Q2 q- `( {: S}5 s- z+ f( q* A1 B) |) @ o
//sql解析模块. H) t, G2 G, d0 B. D+ }3 `; S
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state); S4 ]6 E( i8 _' U- j; E6 ], n3 O9 T$ u
{3 z g1 ]/ o2 a& H- o. @
error= mysql_execute_command(thd);
( D) n2 p8 Y$ s7 p. v; F7 G( w( ~}
* e6 r m' i* L- J8 q, R9 Q' s9 \* t0 B8 C: h4 w$ J9 e4 I
* v: H5 e7 ]% A3 ]& Z, [<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。/ V3 O7 Y5 K, r( M/ ]8 M
- [, ~. ]9 V6 p. h& f, N# @//继续执行
4 _" t. u% f3 c6 D) H' i9 ^3 E5 tint mysql_execute_command(THD *thd)2 L% p3 T$ K; H' K
{
" b$ X6 P; {! d1 X) I( }3 ] switch (lex->sql_command) 3 Q6 Y( B9 w# u
{0 C, ?$ s$ c, n; u
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;9 w) R/ B5 |% d" r# ]4 @' A
* Y1 Y9 \- A5 }
//这个 insert 就是我要追的1 x, U/ z* x: b3 e* a- L
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
: ^; d' n* w3 [$ q( s; ? lex->update_list, lex->value_list,
3 M* w6 l9 a2 m+ T0 C9 G lex->duplicates, lex->ignore);$ O0 R- W: a1 ^8 n% l8 l
}. s8 a. c6 l4 F M& M
}9 o" b6 `- C; R Z! A* K; n
//insert插入操作处理
3 B* h( g' I( |. m2 @" xbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
7 l3 s6 g; h, B% g9 d) u5 L List<Item> &update_fields, List<Item> &update_values, ' m' f2 q% i5 Y9 A& ]3 X
enum_duplicates duplic, bool ignore)7 Y! F* d8 Y- a0 j
{
( \! n, W( i. P5 E: O) s5 W; q while ((values= its++))
: m A; g- v5 b' e O2 @- @8 u {
3 L- f( `" m. t error= write_record(thd, table, &info, &update);/ S% N" X1 {; H& q, \
}
" h/ ^3 k% j) `# T; N/ c$ p: p p}' U' N) k# Z5 ]1 Z- k$ ?% I! N5 O
//写入记录8 v5 ^* L! i( w% h: \
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update), _& B5 T" W; ]( T! K& _; C
{
8 C6 U- a7 {! o6 e/ |5 j$ z! A if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
) T, n! F" x% C, g {" }! }) Q5 n0 T2 y
// ha_write_row 重点是这个函数: f) ]( ]4 t C$ q
while ((error=table->file->ha_write_row(table->record[0])))2 C6 }( V+ N2 j* S9 c
{4 c( Y: e0 a* @( _# K
....
3 u" N* K1 g4 `* H2 R }; a8 m, @" i }% |% Y
}$ v/ ^% j4 j+ |+ K+ D6 v& A
}9 X( Q/ L) n+ L2 H4 s; j8 q" ^
, d! b% ?3 e1 F0 [
4 d& q. ?1 S/ c+ F. g- }" e
9 i4 D, k/ q5 ^! F( }可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
! D/ O& n+ ?7 E, `; Q: l4 b7 C! x* F6 Z- T% n. u/ `
<3> 继续挖 ha_write_row
0 V0 {; f+ `' s2 H. ? r: Y5 z! b- ~& g) P" e: K
int handler::ha_write_row(uchar *buf)
1 b) [# w6 C( ^{
* m4 ]- ?# m# |; F7 T S0 O MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
' s& |: ?$ e: g$ k: z) g0 i}
[, N) x: S1 S8 z+ b- ]& j! `6 @5 W
//这是一个虚方法
* H4 E/ R. d" E2 {* evirtual int write_row(uchar *buf __attribute__((unused)))
# s4 _) \* J6 a{
6 P2 ?6 I" M/ z7 i& P& ~ return HA_ERR_WRONG_COMMAND;
4 U0 m7 S& _2 [( f# r}
" [. { q. ?4 D% ?% [5 z4 T4 W8 X$ p- l7 Q
8 b2 t0 m" h7 }6 m
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
8 D. z& }1 J C1 r# k2 t, ~3 @# M2 p' {# X7 }( q/ n
3. 调用链图
% p7 }$ `6 c7 V0 M这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
& q# x' i, }9 }, E+ V" W 7 m, d y( R+ J9 D7 M6 @
7 \; v0 K& q+ w* U- U" j- J! T+ r/ w% F" t. P0 D
三:总结
) d* Z7 m/ d# t大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) ?0 s. A0 T; g
————————————————
" Z1 a+ v% [1 i2 S2 n8 G" _7 [版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
" [. P. z( _ a原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415% k n; @& ~5 f0 H7 O2 A9 R
|
zan
|