- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55556 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17618
- 相册
- 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 田老师国赛冲刺课 |
一:背景* Z5 j; l) p9 a
1. 讲故事
0 \' H! t* v0 I* ~1 [最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。: r% p+ E2 `+ S* e' l9 _! c. K
' j) B, G, s. }3 h. I二:了解架构图
9 N4 x& Y' t0 Mmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
3 h+ C& j# v! o
* w& x% R! _' ]4 G; [' C1. 从架构图入手3 U2 q) k: |( C7 c) i# Z
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
4 b9 p6 Q7 G! x; i![]()
/ g9 h0 Z& l8 l2 [# \
$ u: B1 e6 l3 \; \+ i. O) i% g) O# _2 v" f* L0 U% A
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~. {' e) ?( W' s3 i
; y3 y1 S/ G4 x* m8 {" t
2. 功能点介绍1 x, G0 F L8 ? C( ?
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。1 [$ B! q- P0 z9 S& v) _* T
1 e. Z# ~( C) ^' y6 M4 l* L<1> Client# l( q$ n" E) g' V
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。1 r- ?9 j" `" H9 u; l: P" }
6 D" W8 T3 [6 A" Q& b+ `4 k5 s
<2> Connection/Thread Pool
7 I" a- L" h- cMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。- ^; E2 d* e1 R3 d2 e+ }2 g0 K
( W! T6 r( s% D V1 P+ _! |$ `" {
<3> SqlInterface,Parse,Optimizer,Cache7 O/ J# M& {& n& s$ j* P
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
: @" k: L" E0 `- G
/ @* _! R# P0 x# `# `<4> Storage Engines% }+ a+ ~- R4 ^5 z* c6 u1 e
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
8 S2 j! j. h \. j- E: w" X6 E( }
三: 源码分析' a9 O- a+ q6 `( ~( v- R) t, U
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。) t E+ |, X3 c- t1 x) H9 y1 V
+ Q. v2 p1 Q, f& J8 c) w1. 了解mysql是如何启动监听的
5 B f* R- k3 f1 x/ ~9 ^8 V手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。: w- P* ]2 g& i6 f
1 i! N. R$ X: _9 J9 x: X1 h/ i) t
: J# p2 b! [+ A& N" |! t2 E
+ [$ r. b' _; k8 i
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。. G9 H1 j4 \ K$ W( T: u( M$ `
, f4 M+ N5 Z! O4 h; M<1> mysqld_main 入口函数 => sql/main.cc
$ J j7 z& @% I. _5 K% m' P6 f
, M0 a Z) q- U7 B' h$ ]% E# h' V# o! j
extern int mysqld_main(int argc, char **argv);
% Z7 Q3 c) y, L& V2 h# ^4 N
8 S6 \; O/ T8 _int main(int argc, char **argv)
$ d0 j$ e4 I- l( {; ]" N2 d: f{
4 P% ?8 B, J8 V$ Q" e8 f return mysqld_main(argc, argv);
) U3 P' e$ M% Z}' M2 f+ P4 H$ v' ?( n2 x1 f2 l
3 [8 ^- t( w; }4 l3 A5 O& h4 E
7 x: o, a o3 g- B* t
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
# N' v1 @5 M! R' ^( b; T7 s# ?+ g8 q. N+ h1 _5 g3 x
<2> 创建监听) z9 n1 [. ^5 x2 }& X7 s% L' y
: A4 X, U. `4 u, P
( o, L, O4 V% Iint mysqld_main(int argc, char **argv)! y2 x1 l1 ^" `: t
{9 o, Y! [3 \% @5 v0 t B: H. M# L
//创建服务监听线程- P% @ L# Z- G+ E, H3 d
handle_connections_sockets();1 n2 r7 y* |4 q0 j9 N
}: k/ X+ A' p* t+ @7 h
# E& q: v8 o4 ]# |
void handle_connections_sockets()3 i7 E. o/ m) c% C
{/ ?# _% n" k, z k" M% p
//监听连接
( q! V% N% T. ^, B6 q0 I+ N new_sock= mysql_socket_accept(key_socket_client_connection, sock,. Q Y7 T4 ]& e( x: E7 v
(struct sockaddr *)(&cAddr), &length);
7 n" @, f& M5 ~
& P; T* U* y/ ~9 m0 R if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))5 `. H t2 v' @. t: l$ R
thd->security_ctx->set_host((char*) my_localhost);
* h+ x6 d- a% c k& v
" o, y" @" k$ j2 z: G8 V; H% m5 C //创建连接
6 c) G; e, o, V" F8 G# N& L create_new_thread(thd);
* @/ i S2 d$ G/ U, V$ G}
$ I6 y& }% D. q' K% U% j3 e2 z t, Q% @5 }( G0 h) N/ Y3 r
//创建新线程处理处理用户连接
, |. ?; { v9 `) qstatic void create_new_thread(THD *thd){
) V6 p) G( k; h! p7 _# p" I1 ~- `) u( {" o& j' F: b% F
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
. t5 \2 \- F# n$ Y; u6 g. d& Q$ N8 F) Q
//线程进了线程调度器8 Z* e; o; j! ], v5 h
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); . z' A8 T5 Z, r& R0 O' A* x
}
4 l* g# y2 k3 d# A7 h$ F8 ?6 [, Y G3 l/ \) t
5 A; g7 t2 b7 i
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
g( p# c) ]. t. ?& I9 q1 U$ B5 Y2 F1 c
( R! o2 |, q8 l( @4 S! y4 d6 u2. 理解mysql是如何处理sql请求
. _/ s7 `$ L% N5 D( z* @( s) h9 |这里我以Insert操作为例稍微解剖下处理流程:
3 m' p: ]( t- h8 k! ]$ Y. q5 |6 Y/ B5 ?1 j" ?
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。5 R& }; e, V8 v
9 r/ q! t5 c# K4 B5 N/ h$ K# S: w# s2 {( }" w# y
static scheduler_functions one_thread_per_connection_scheduler_functions=
( Z1 J, L. U( C8 J, y' @, h+ Q4 f{4 e4 N3 t+ o4 T( A+ A
0, // max_threads9 G& Z i. X; o8 J5 [- k$ D4 u1 }
NULL, // init2 f/ h$ l3 a1 b3 S
init_new_connection_handler_thread, // init_new_connection_thread
. W; I9 G$ S& M& p& T create_thread_to_handle_connection, // add_connection2 M/ i% R3 ~' U1 g2 M* _: f
NULL, // thd_wait_begin
& j3 f0 q0 y3 P NULL, // thd_wait_end
$ o0 z6 B- f- t7 v0 J NULL, // post_kill_notification
# o. ~* b- k. n; `, E* V0 C one_thread_per_connection_end, // end_thread
- v: L- h7 t3 s% p! N; d' j6 M NULL, // end* W9 n2 r Y4 Q- Z K0 d3 b
};
8 A$ `& }, g" Y; G) _+ \$ ~% x& b& _ ]9 Y+ y- F H) B
; L0 g( K+ y4 E# [2 a8 ?* _. @8 e2 |从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。+ I) N# ]7 w9 q8 A* x, Z
! b+ M" ]% O! Z- \- G4 t5 \1 z+ ]
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
/ q' }+ V( x# _& G
$ _2 N. o/ S! X Nvoid create_thread_to_handle_connection(THD *thd)
" ]. M Z" V! j8 d0 {! l; }$ k0 e{. |, R0 `. [9 b1 K1 w& i8 i
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
3 _3 Q- E5 F7 a% W" n& t# Y handle_one_connection,(void*) thd))){}/ \) I, [- x" @ [, q" Q. o
}* k+ X; Z/ d% X* ~7 A9 V
//触发回调函数 handle_one_connection
9 F9 P: Z7 S$ T. dpthread_handler_t handle_one_connection(void *arg)) H7 }; I1 f2 _ j
{
; A |5 d% z9 A; A# S! l2 V* d do_handle_one_connection(thd);
7 F2 C( u# g3 ~$ m- E}
8 z$ x% I @7 P$ [//继续处理
2 S1 z4 `; o4 T% gvoid do_handle_one_connection(THD *thd_arg){4 Q6 b; v) U& t
while (thd_is_connection_alive(thd))7 m; s/ W5 t. S, m8 q0 f
{/ B, r, [# ~3 h3 |4 u/ z
mysql_audit_release(thd);3 ^: r1 J. K; I4 S
if (do_command(thd)) break; //这里的 do_command 继续处理
6 j% }8 j% J6 r4 f8 F( ?& `4 D! b }
$ a. O5 j: i! F}
3 U5 U+ i% V0 |//继续分发: K* r1 C& n" P/ x: u% c$ i
bool do_command(THD *thd)1 L, g4 {9 p" l4 K1 q0 @$ a$ `; t
{
& ~9 w; J4 ]# H; t' _- G$ ~' Z8 D return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));0 T, P$ Q) W! ~
}
2 {! T/ I1 a1 C9 I8 z+ Wbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)8 D, P! m4 j1 U; G& |
{; _8 k& G" V5 h2 a
switch (command) {( w6 J& z6 u! s" ]5 e* T" u
case COM_INIT_DB: .... break;; A6 v/ W5 O& ~9 h
...
. ~1 ~4 G: b. Q8 g case COM_QUERY: //查询语句: insert xxxx
" `" H1 X0 [/ N, G mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
. }$ L) I9 J% t1 S$ I, S break;* V. Q$ ]" @8 r; ^7 j2 L
}. a& x0 J$ T$ ^ M1 V2 U; T
}
: w t# P2 ~# |) {" A' V: U//sql解析模块
9 b9 x9 H( A, O; h/ E8 _void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)8 @# H% }4 P' b# `& E( E. X1 H
{9 ^$ P& s( M% T4 C0 o
error= mysql_execute_command(thd);6 C6 ~' k3 I* x* S& R. o- e7 ~
}
8 ^+ V m7 X# {. Z7 H! B/ ]4 {) Q0 D s6 D
% J9 u2 d6 M1 O/ ~5 g<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。5 q8 T+ `$ l1 z6 i) M
( ~# x" @' e7 Z, s2 O- D$ o
//继续执行
7 F& L5 W7 D1 b8 G: G+ [, o: zint mysql_execute_command(THD *thd)
$ y, g9 c6 X! w3 x1 z Y{% p* [+ q4 `! G% U; A' s0 F# y# a& F
switch (lex->sql_command)
/ X1 w8 U" ]& v9 A% y3 ]* S {
9 O* @, w6 B1 | case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;2 v8 L7 a G' a& C: b) ?
y* Y6 X$ V/ z7 | //这个 insert 就是我要追的
' E" d. r% r0 ]5 b4 c1 j case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values, r$ Q' n, `& U
lex->update_list, lex->value_list,# h6 b X' m9 `( J/ d7 k/ f: m( r
lex->duplicates, lex->ignore);
( H$ h4 a: @! \( |5 F }
6 @5 x+ W& [1 Y, M8 ?) K% x: B; z: ^}$ `" z# f+ C% T6 q5 v6 I, Z2 A
//insert插入操作处理. k" _7 ~; C( D+ J6 q p
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,0 K% E) n: X) L
List<Item> &update_fields, List<Item> &update_values,
- i' B; B3 @2 t+ j- _# ~8 u enum_duplicates duplic, bool ignore)
% v7 I& U r0 C- E$ ?5 l' R) C% Q{/ z. x1 C4 E4 l! ~
while ((values= its++))
4 F8 H7 Z2 Y3 b9 ]/ M u& i {9 R; i, ]9 Y" L- L( b( F) m! o( V
error= write_record(thd, table, &info, &update);
! t; L+ b, K/ \: y7 x }9 v }
% ?! N' X. U6 L/ z; l7 P}
6 X8 u: S! F6 }# X5 r t9 i//写入记录7 P" w4 I; k, [: S* _) o1 g( [9 ?; v
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
& r$ T6 H6 P e{
1 d% K \9 u4 X1 k if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
8 E; y9 r: x2 ^0 Q, V1 V! _ {
7 H* N+ ^& @# M- ]" s* P // ha_write_row 重点是这个函数
& c% _; A. C) a0 f& P while ((error=table->file->ha_write_row(table->record[0])))
7 d% y4 B' x$ S5 l {
r9 c, l8 E; ]* Y ....9 J# {2 v4 {* W5 R, P$ ]4 p' m
}, k0 i8 U% ?) y# |9 N1 i
}
) w; q: b9 l# s! y/ p}( M7 v/ [! L- O$ W* e& I
; w' h; m( }3 ^6 k5 n4 |$ s. j6 C0 y! Y
0 r7 d* Z/ P: A* ]
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
8 k8 V" s( Y& O
9 J9 j K* h- k. B% ~/ q7 R<3> 继续挖 ha_write_row
, ~% G c% ?* p, c! I3 X. l5 ? b5 ~
" l1 @! W. U: E6 n. Z. pint handler::ha_write_row(uchar *buf)
. }- a5 O+ s. L; x- l( w; w& p{. f# e2 H3 F( s; V$ w
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })+ ^0 x# @: N* U7 i/ T% o( f
}
8 p( Q1 j% X2 ]4 [6 y4 B
7 L1 k6 n" o( t& I//这是一个虚方法
* S7 _5 E2 p* d9 ?# X, X3 `virtual int write_row(uchar *buf __attribute__((unused)))% t8 M# p. s6 u) p& T' d4 ?1 U9 J% N
{
. x" j! ?, [* j return HA_ERR_WRONG_COMMAND;0 F& w/ R! { f2 K, I) J
}
- T0 g t6 \ L& a, L
$ \, V7 r; o7 u$ j, e6 m0 b" l: K: B/ `) K
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁4 `9 r- }7 J; F- b
$ h/ e5 u# x, V; e: K
3. 调用链图
6 [# Z6 w+ e! v这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
+ U, C2 X6 M9 F; ~![]()
$ l/ I4 F. v. }% q9 Y' A1 @6 n$ N! m& n$ O+ @3 i% Z6 L, ]. r
! Y v1 y2 `& C1 x' a
三:总结
& Q" L! L1 y1 D大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) Y S. Q1 ]8 w" ]6 \7 K
————————————————
* h, l$ \# Q7 C! n1 m* L1 W+ r版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。& M# z: c7 j D* i- p
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
- O9 h6 N2 p, H" }) j& `+ _ |
zan
|