- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55580 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17625
- 相册
- 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 田老师国赛冲刺课 |
一:背景
* z1 g" U9 w; q R) n1. 讲故事
( Q6 R* O+ ]4 t2 c* a; a* D最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
% @" Z X& u% A
0 h! {9 n% z4 c$ t$ |4 [二:了解架构图0 D. i* a& W9 Q: p6 `# g
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。) O7 [$ l2 r& c8 R
3 S6 y/ n' ~2 M( V
1. 从架构图入手. H6 ]" ^2 m& ~1 r* z
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。0 Z5 Z N& f+ E/ o1 p$ D! [5 g
![]()
# ^" x0 v2 b; t; V1 `6 e* P. m8 U
# ?4 ^ L C# d7 B3 o7 R
- `8 r: I: [- X! m8 [其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~- y7 T- q1 ^' I% V
0 W6 P( Y5 g: N* z6 s, W2. 功能点介绍
# c& V$ ^* V IMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。0 y0 E ^, G" E: o" z9 t
2 V; v" }- _4 u& [1 m# r, ~% |
<1> Client
9 X! [5 Z( J' X, E- |* o0 d不同语言的sdk遵守mysql协议就可以与mysqld进行互通。: z& T7 f k. K$ J6 G# z8 F" r
$ \+ h7 F4 S3 f$ }4 [+ o<2> Connection/Thread Pool
# S. }+ P; j4 j9 c; v- c! ZMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
0 D& d4 N7 l6 Y5 J( p1 Q2 y' J v) d# A' J7 I. f- X
<3> SqlInterface,Parse,Optimizer,Cache
; j: k4 ?2 O; u- m7 I对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。+ p" _) e0 w' N! }3 V
- _( Y; m! D3 o<4> Storage Engines. N+ x& ]7 ?/ W* { ]
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
% p5 J2 k/ O* J9 l Y- A, K/ o( }4 Y
! F4 r$ X. y& p三: 源码分析* R- X) F3 s% F" W
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。5 r8 x4 L$ j3 R
8 ]* @3 K& _$ L. e
1. 了解mysql是如何启动监听的
3 J! K; x2 f3 y4 p手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。$ E; t3 y" c8 e" M
# D" h* p# A6 h' O$ {, y K6 \
& a4 r5 r- ~# j" m0 \/ `7 p* ?) [8 G- U* n6 B: j
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。: e2 m- C/ b& i) s
/ F+ p/ f7 j& [, w; q( W9 Z) b* ?<1> mysqld_main 入口函数 => sql/main.cc
0 M! v7 x5 k q# \# A! Y; j/ R: n* K, v$ M4 J4 z5 c$ A
' s5 ^* c% N- n( s( p! p. rextern int mysqld_main(int argc, char **argv);9 m( n$ A3 x8 j9 M1 b
4 v* ?- ^: w3 Q, Oint main(int argc, char **argv)" J$ I! z( ]1 J( H/ U
{6 {8 b! A9 P! l8 U& ^% h# Q
return mysqld_main(argc, argv);3 A& I8 l5 ] [& `* u7 c
}
' g& O% k1 c, \
3 z) ~7 s- }) f; j, {& ~# m
4 {# n$ O: _* G" G8 t9 }0 A这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
g; q W6 F- o% H7 e% I
+ ^/ m4 y% y! u6 r+ D/ s<2> 创建监听
! S# ~1 ~4 s# q) i9 j: ^; }5 k5 z6 R
4 e$ j7 ?1 @0 Y" m
9 ^; u) i' |6 p4 w. }( D' Y+ Tint mysqld_main(int argc, char **argv)
/ v k2 f' A }2 `5 I+ h( n: ~{& {& p5 }1 v y$ T8 x# N
//创建服务监听线程9 ]4 y. R! O; m1 i- ^; K. E6 {! _
handle_connections_sockets();1 G7 {: @+ _/ M( [3 C/ G# D
}
4 Q* Q3 Y5 u! ]4 g' q# }' M2 t- B( f( ^; E9 n
void handle_connections_sockets()! S! |0 m5 R, ^) K% P) e4 Z
{7 ]! g. d/ j" b1 _9 M% _% [
//监听连接, P- c7 \1 r4 s0 y) H& Y: Q+ ?
new_sock= mysql_socket_accept(key_socket_client_connection, sock,9 I M# E9 D$ R' G- a' o+ m6 M
(struct sockaddr *)(&cAddr), &length);5 z I+ K5 X' f: g% x8 C" z
! t' {: {1 A3 d; H* M3 U
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))1 Y8 g4 ?' F( x5 i5 I
thd->security_ctx->set_host((char*) my_localhost);+ ~5 v5 p3 @$ [- l- Z2 O( N
8 V5 J p8 c6 Z/ q$ I, _
//创建连接
6 a3 U- p8 o8 \4 V* h create_new_thread(thd);
- U4 q( \% w3 N/ u}
! ^6 K$ {; L; R$ M! r" i" ]" @" R5 }9 {( p4 y" \ w; l
//创建新线程处理处理用户连接' K3 ]0 v7 n3 U! T# B
static void create_new_thread(THD *thd){
" t. j: |2 a" _6 L
( |( x$ S' w) t- x- [! F8 k" z thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;' v- z& a! q* @' X
( p: ]* v6 F: I( T
//线程进了线程调度器$ @3 p& l" D" d) I$ k
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); ) c; ^ f8 W/ i6 p6 O- g3 O
}
% K! S ~) I- s& v, b: Q
1 h$ \) {; J" ]
+ @ a o. }; z. I至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。' `3 F* e; n! `" G1 Z
& @9 C3 K3 T) m0 [' S) ^4 t% I5 Z5 M( B# ?5 @ F
2. 理解mysql是如何处理sql请求
' |; ^. ?. \0 Z4 f' q8 h这里我以Insert操作为例稍微解剖下处理流程:
( Y* X7 [% c. h8 A5 E" ^% ~
* {& p: d- s! W1 m7 |: G/ g当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。! X3 P" a( k0 @8 t0 a6 z3 b# t
0 Y- f8 @( P Y, w
& p" i R8 f$ d5 x, v, k) p$ o
static scheduler_functions one_thread_per_connection_scheduler_functions=
$ `" `/ H3 K0 {9 v/ d3 C& h{
$ L, D5 H" A4 [& Q& i+ { 0, // max_threads
1 h0 u* t+ V) v4 X$ r7 d: p NULL, // init/ [9 k' z. @& Z7 _
init_new_connection_handler_thread, // init_new_connection_thread
1 X$ C' Z' U6 E" s3 s create_thread_to_handle_connection, // add_connection
2 l" s* v' `; H NULL, // thd_wait_begin p2 k1 X8 y; A9 T* G+ A
NULL, // thd_wait_end. z# Z; p/ ^5 h4 a( f3 Z
NULL, // post_kill_notification0 H" [% _5 b% h; l3 x8 X' z
one_thread_per_connection_end, // end_thread
- T0 X/ P6 v5 D0 m' B& h/ f NULL, // end
E) A$ ^1 P7 |# y7 _ Z O- e};6 p9 W) _9 L6 v& w5 m4 J
. d: t, K% D# [# Y0 z
5 q- G3 Z$ H& i7 }: O从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。7 ~ J/ H. J& _: x8 Z
. a; g7 N n+ r<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
+ H# K% Q9 D8 D: W% D1 K0 D) C4 y( V
void create_thread_to_handle_connection(THD *thd)
( @; h# i j4 d3 ^$ t) P6 i S. y{
4 D" d0 s2 v0 `# p) k% C- H if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,5 J0 M7 N. J4 _9 J& s, v x
handle_one_connection,(void*) thd))){}& r: }$ X9 \! o7 l6 w
}2 i/ I* H% X% n; N
//触发回调函数 handle_one_connection
" R6 \8 s5 }3 R# `+ npthread_handler_t handle_one_connection(void *arg); z# Y8 ^4 d2 u
{( q1 ~) a! W$ l3 Y# H
do_handle_one_connection(thd);9 D0 L6 {9 r% x* z$ I& E" ^! o
}
* N% X) X" O9 D" J) I8 X//继续处理! w2 K" ]1 Q( A6 k/ f% z9 t( j
void do_handle_one_connection(THD *thd_arg){5 S& | F* j; d' Z8 m0 r
while (thd_is_connection_alive(thd))
- r: R- F% n; k, k8 s/ y8 v0 ` {
0 d9 w8 V9 }6 c" ^# n mysql_audit_release(thd);3 Q1 |4 X4 x! K7 _. W& `
if (do_command(thd)) break; //这里的 do_command 继续处理
: d/ B$ W" P0 T, s+ C }
: g1 _9 j! O9 Q}
1 K! I0 o) }9 Q. a, w$ K2 u7 A" y//继续分发
, h+ W- z* J) x9 R: a9 ?$ I Zbool do_command(THD *thd)
7 Y6 B; K! v0 t6 K! m7 U: I+ G{+ b8 l) e9 j! C
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));, v/ ]8 N- R$ @6 m n
}
! ]5 h- O) U9 @8 Z4 f1 tbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
( H6 ^+ x7 N1 L" j r& m6 u{0 L# ^1 w, [/ u; X! e
switch (command) {) m1 `7 T: v! B* p8 A* b
case COM_INIT_DB: .... break;! L5 r+ }3 i# s& h/ J# X
...
6 U" D/ f& {; w5 Z! X, H case COM_QUERY: //查询语句: insert xxxx
) N3 t0 |9 N6 I2 [) [ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析; d( C8 M" }) D( g* ~* S+ _$ G9 q
break;5 Q, D$ ~! g- D+ Z7 D
}
5 Y: k& r1 ]4 V9 S4 J}1 N F E1 f& l8 h9 i' ?! }* }, K
//sql解析模块+ M3 i* |7 ^7 V, G
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state). l* u" j& k v' t4 C u
{8 ] A6 v) {' x
error= mysql_execute_command(thd);! U6 m% p/ n$ N
}
- X: _. O& c: [6 x
$ h" ]7 I* y9 I9 P1 _* N: v# R6 |. \( u/ J& B
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。0 d3 T- Z; I$ b
4 ^) s' l; M* p0 w
//继续执行- N1 I# e7 N8 T9 {6 b
int mysql_execute_command(THD *thd)' x- g$ s) c4 U! n# p
{" n. _7 F: @- g: q
switch (lex->sql_command) 0 o+ }. i- [$ x8 M
{
9 w$ a! h" h# n+ q case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
8 {0 Z* l. W2 C+ T+ C+ d
' ^+ I. r/ {, D: q; W //这个 insert 就是我要追的
: [$ l$ O+ d- q( }- R case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,! \9 f/ I8 Z0 l9 D
lex->update_list, lex->value_list,
. I. I6 p* ~; F lex->duplicates, lex->ignore);3 ]8 a- d, s. D9 l& j, F
}
4 P. ^! P) E% n, z3 P}2 h" p8 _' ]8 e
//insert插入操作处理
# }7 S7 ?$ R0 z' i; t; S* }# Ybool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,& z9 \# {( w9 l* v+ M) @2 @( G" Y* _
List<Item> &update_fields, List<Item> &update_values, ! e" s5 [2 X7 q. o& d
enum_duplicates duplic, bool ignore)
- @$ k0 a1 g9 L{
% e6 i, V* u: j$ t while ((values= its++))2 p3 g* h& S9 y6 O1 `: h
{6 V h! ] m" s2 E) b6 J, I
error= write_record(thd, table, &info, &update);5 ?2 S3 e; N& R8 e$ g, D/ v
}: n4 \& p+ k# w3 \; Q/ m% T
}' G( M+ Z E9 C# l! z' K0 |- J; T
//写入记录
8 K6 `7 q: s1 V Tint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)" u( O, e* B1 V4 V* G" |) d
{
3 R& n# g9 _( O% [ if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)& y3 l9 B+ r k
{4 J5 [$ Y- E) m6 T5 Y4 I7 ]- z: S
// ha_write_row 重点是这个函数
! [9 v7 }0 `; O2 Z y0 E4 } while ((error=table->file->ha_write_row(table->record[0])))% J( Q2 p( h# M2 F4 x, n
{: c* ?; f2 T1 g$ g8 P1 K: @+ U1 ?
....
3 h1 Q# V1 \9 D. H$ H }
9 v# b! L+ T) H }* i3 t" t) L) w9 \
}, Y) Q: b0 G% R/ u% e9 Y
8 V0 a5 ^6 h/ ] c" l* s2 ?8 c7 A. E
* u: }# w# j& {' C/ J$ O可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。; q6 Y8 X- ~! b r
" E% z* k" q/ b; U- i/ j9 }5 e1 E I<3> 继续挖 ha_write_row% a- \/ b) j6 T7 s; P
0 l9 F: \8 u) w$ S5 u' y9 xint handler::ha_write_row(uchar *buf)9 [# F. x0 R8 C+ A4 k) v% \+ J/ I
{; e z- ^# N& S" b' ?, Z
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })2 o7 ?6 F( r9 e0 e7 F) u% f" v
}2 T$ R$ {; [6 Y: ~3 P
2 J7 V" \! v7 y
//这是一个虚方法. V3 [! x' s g" M
virtual int write_row(uchar *buf __attribute__((unused)))2 g& M, @. l q
{
- C3 X" P( C' r7 j- w1 k$ | return HA_ERR_WRONG_COMMAND;
# g. i6 {! P" v" t( K ~}
# s# i, c: U. @( A" l) Z( T
4 c4 Y* @/ z. h8 r" d7 F/ N# K( M0 F: p3 Z5 b
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁( V& n4 x6 h5 _: j. Q
( C: [+ H# N& c, l4 L* i3 ~" Q! R. x3. 调用链图
! z5 F- S0 J3 s0 d- T! q9 ?这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。( W% g2 |$ K: Q$ ], u
![]()
' t6 t3 N/ v9 `4 ^5 S
9 c. v( X0 L$ m f5 a; O
5 O3 l/ c! L- U+ o6 y1 U, x. H; C三:总结
* q N! e7 c. \0 d' l b5 M大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
9 f: k: N. V! P1 U————————————————$ I4 ~; ^! [' A8 R! p9 K2 k9 A
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。' T9 M" B( v9 S+ J5 z2 \. }! @
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415# m% Q; v: Z! ~/ Y
|
zan
|