- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55510 点
- 威望
- 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 田老师国赛冲刺课 |
一:背景
1 R% }2 U5 A6 ^% ^3 _1. 讲故事) @4 o V# U+ L3 K- ]# J5 N
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。" M5 J4 f& g- j3 g/ ^
& R4 I! M! b0 ^7 x% K
二:了解架构图
. n" A' [/ A$ Q" T% imysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
7 K* b; V/ d- Y9 @& g" d1 l5 u/ ?2 Y& }- f* J+ F' V- _+ M
1. 从架构图入手
1 W/ S: j' k! u6 \4 f, K. {0 B1 [大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。3 L' U! ?( ~2 F
5 n3 r/ L( Q4 [1 T
0 \$ m* W6 r: I$ r
4 F) b' G; o/ H8 a' K其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~/ e$ `8 d! w: I) B5 L% `" K
2 w0 `. h' S6 d1 A( q0 i7 r2. 功能点介绍
: ^& a# ^3 P2 z: V& bMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。# f' n& _5 W* {1 b: b: j& Q6 U' {
3 i; x( f8 W( ]+ ?4 g0 X
<1> Client
3 \- z3 r0 O: q$ Z不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
( e. N4 [& a# ^3 e' I/ Z d4 K7 [& y: ?* b! N% A
<2> Connection/Thread Pool: M3 n6 d4 x3 H' Z4 x) d/ w1 L
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
5 ?$ D0 r% R- L9 U3 k
9 S x8 s* ?$ c a& a4 m. h5 d<3> SqlInterface,Parse,Optimizer,Cache
\! p$ ]3 C8 T7 U' b对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
' L3 X# {) e# P
1 w) R9 ~2 Z+ c) k<4> Storage Engines
4 m8 K5 f* L. ^- y& h6 B; a负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。+ j' ]0 ^* Q; |3 _5 N3 F4 l# E0 @
. S6 |$ H" o& n8 J" `: {三: 源码分析
) v; C+ ]6 E9 d& ~( P2 n; B, ]关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
5 @, T9 t! |" ] i% d# r& c
) ^( E) T4 f, D' V1. 了解mysql是如何启动监听的
1 b* w0 R! [& ~: K3 F手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
+ y0 U/ d! ]: O3 o, @, Z0 p, Z! w![]()
$ H! k5 S0 i% U: X, ~0 D# a' A( I- x6 w6 d
' X3 v* d. l k6 @从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。4 r+ V) S3 J/ N g, u& @2 _
6 e* I( Z1 r8 [; X7 }9 e5 Z
<1> mysqld_main 入口函数 => sql/main.cc
4 n7 V+ M, }2 x5 v: U' m
) b9 y: o8 r5 Y# b- H0 m( ^
/ l8 ~! O8 P; @ c8 W. s) |extern int mysqld_main(int argc, char **argv);
9 @ k! i4 [, J9 R* b3 q# j6 h
Q& D7 A# {+ C# R/ |int main(int argc, char **argv)9 _" z0 K/ }7 C c. b" O
{
( q1 L; f/ ~4 |" {" M return mysqld_main(argc, argv);
( y. z5 i5 T# B8 r S% f% ~' w- j}
3 n# ~" M( V( k6 J6 V6 _
+ T9 M- C7 T6 f3 ^
0 y$ B% M* ~0 U2 K: D/ \% f这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
, B) L" ?8 U" g9 W$ s8 q; E. v" \
# Q. \7 E, O2 `<2> 创建监听
+ U: \+ \; E' }( d# x
- p1 @4 M5 a/ O/ I# g
: D+ H; i1 Z/ x5 o$ u Eint mysqld_main(int argc, char **argv)
. ~9 u4 T* K9 G$ I1 X{: @8 Q$ q8 O1 g6 J
//创建服务监听线程8 m9 p3 B( l9 \ g7 f3 a
handle_connections_sockets();
, O+ X( p4 @4 t" y; }. F1 w}
0 W5 S g# M- d$ N: k# q4 x! y, D& \! o1 F
void handle_connections_sockets()
% B) Q7 h! G; A2 H- A# N% E{$ p) u, O7 |7 e
//监听连接
# K! r7 s% H; h- n! Z, m new_sock= mysql_socket_accept(key_socket_client_connection, sock,; z7 V9 @# W2 K1 P7 ~% N/ ~$ F2 i% Y g
(struct sockaddr *)(&cAddr), &length);
) |( D8 j5 A7 p8 ~3 k2 ~
1 n* w0 S# X- ~4 X. {3 Y' Z if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)) }+ W/ |* A: Z8 \1 Z
thd->security_ctx->set_host((char*) my_localhost);
7 \( b" j% P8 Q
) K" C& k! N9 K- G8 ?( }$ d //创建连接
7 y) m, L5 q7 a9 n7 f create_new_thread(thd); T, {# c% U* C9 ?* `- x
}1 V6 Z }! z, y1 h2 J/ N
, h# }' m$ h9 [. v2 Z//创建新线程处理处理用户连接7 _! }: j' i; q2 d: y7 N" {/ C
static void create_new_thread(THD *thd){
! O# ]0 v$ T2 U* ]4 ?7 R5 ?/ A- e [! n3 m( ^' D r7 L9 t" r
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
3 I* X3 B$ z+ f' A9 J
) k& P" t4 G& d //线程进了线程调度器+ l A/ \( q' y& |
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
# Z" J* p# z& H}
* @, t5 g4 R$ m+ U8 x2 N% c0 |, R; Z4 d: Q% F. Z. [: S q n
" x7 h( K9 b8 v7 W! c至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
6 f/ J( i- h3 i' _; I* O8 Y/ ^7 C
3 O# x7 N- O; K; o- A% t% {! a6 H" K# S
2. 理解mysql是如何处理sql请求# N z1 Y/ K- l4 }3 s6 {% q
这里我以Insert操作为例稍微解剖下处理流程:
% i. y9 P; e/ B2 w3 }4 {9 j
' [1 A1 C' V9 N6 h% D) L当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。: }% d2 p7 Q0 C+ J
* h5 [3 ?5 Z4 M
! z" A& E% U/ fstatic scheduler_functions one_thread_per_connection_scheduler_functions=
3 X; o) b8 \! B0 I x* Y1 E+ k{; V; Q' j* ?+ }; @$ a
0, // max_threads
/ b. F! T& U3 x1 }7 t NULL, // init- o% Y* V+ W# J4 i; T( ~. w
init_new_connection_handler_thread, // init_new_connection_thread
4 W# b! \$ a' C6 L: G3 E create_thread_to_handle_connection, // add_connection$ X5 I0 ], \( }- d
NULL, // thd_wait_begin
" f$ p& W! I% B: W NULL, // thd_wait_end5 r4 ?9 g) n9 M9 j7 z3 G
NULL, // post_kill_notification
- S/ l; G L0 V" H one_thread_per_connection_end, // end_thread
8 ^% }5 O! e/ q7 J NULL, // end
$ N& W- J8 L6 W% Y3 P};
' u( h) n6 I. {; n. S* t; x
* E4 T9 O7 d/ d! w \+ b) u
" ~) j" S ^' v( V b! }8 @( n从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
2 l' E. ?0 o( o7 e" ]; k$ m2 C9 I. Y$ F" {0 Q) s
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪: ]# t4 J2 B! {1 k1 K$ q# ?% E! g W
7 N+ f/ J' O; s$ @9 R
void create_thread_to_handle_connection(THD *thd)( G7 W. W" N9 X! Q+ h7 o& M
{
c/ e- ?3 Y$ @ if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
0 M/ J5 ^: Y& r+ j handle_one_connection,(void*) thd))){}
% j3 S; Y" i' p: |. l+ l5 U}
( B# M$ I; ?7 _* G6 _//触发回调函数 handle_one_connection" r; ]& t) [" t6 m' f, F0 F
pthread_handler_t handle_one_connection(void *arg)( V! X+ L- G. |
{1 y) S$ y0 [& x' G
do_handle_one_connection(thd);
$ m! d! M) ]$ G" f1 y+ f. ^}5 F$ y9 w/ s* U# g; T$ s
//继续处理4 Y. P) X+ L4 v5 v; j, k; h$ h; O5 {
void do_handle_one_connection(THD *thd_arg){
9 g, U# O- x! X0 O while (thd_is_connection_alive(thd))4 V/ R% H; W9 D7 o6 I
{
' g: x2 B& W: U- a: Z, S5 A& k& Z mysql_audit_release(thd);
( h4 p, u( M3 I4 y+ ^ if (do_command(thd)) break; //这里的 do_command 继续处理
5 C1 \; C5 m7 |9 D }, V9 E. F1 j" i. c6 p5 `3 s
}
- m1 V a# E% _9 z6 d/ g0 V//继续分发8 C+ n' _6 h4 L/ X+ W3 X
bool do_command(THD *thd)
8 u6 g1 H& l7 N- f3 G) l{
5 ~0 O4 k K0 \$ r return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));3 z8 d& E) z4 c& G0 o# n: V% m
}% ]! Y: T; w- {' ]
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
( }+ Y% m. I, s# i' X* t{7 {. o7 ~" N- o% y& b
switch (command) {% f# l* }7 ?* x' {( |* O# K0 l# T
case COM_INIT_DB: .... break;) m4 Y6 R8 f" \0 ^
...% H$ V8 c- i, _ W: |$ c
case COM_QUERY: //查询语句: insert xxxx4 C/ I* j2 u/ ], q& r$ M" x, e) S$ g- L
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
- I* N8 A2 N' k break;
6 L) k& |) Q( w8 Y }
( N3 x7 g- U, }& G( g1 m! S/ p}
! b n. \2 R. k6 g; Q$ X1 o, d" \& k//sql解析模块# _8 b; j9 M+ T N/ n! x o
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
1 G( n& g% o+ M4 f9 `+ T{
6 H; w; |; L1 p- l2 s3 ?5 d error= mysql_execute_command(thd);: J! O5 v4 h* T3 l9 z7 |* P" S
}1 A3 B {- ]5 \- X: _+ z/ G5 _1 U
3 a; i. C$ V; H
2 ^9 e8 \ L6 j2 }/ Y<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。; W9 l8 d M( T$ g. N
6 Y: G! H1 N" k" k* d( Y
//继续执行# f9 {- g+ A! Y" ~) e
int mysql_execute_command(THD *thd); u8 w) p0 \. {
{2 N2 }; }4 B: Y, `/ e3 L7 s/ t
switch (lex->sql_command)
) ~$ `2 h: k3 E {) P, G. Z4 y$ M, C
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
; o& ^$ _9 O6 O# q
" ^" s: t8 j! V5 ? //这个 insert 就是我要追的5 z- |4 w1 x' }- G( |
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,1 o# k; P" q' a: F2 K+ a. p N
lex->update_list, lex->value_list,
7 @% N- d* ]6 H# U& z lex->duplicates, lex->ignore);
' G& `6 T2 k$ d" i+ Q+ F9 k }
8 a. e# }# ]# K7 R}
+ |& |! s7 G+ P* U8 B$ q//insert插入操作处理- T) ]) |5 b/ b( C. _/ R7 e
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
$ C. ?# b- s0 q List<Item> &update_fields, List<Item> &update_values, . b, _$ F$ e$ c9 N8 u
enum_duplicates duplic, bool ignore)6 H& Z7 x6 T) ~) I& f( G
{( ?! S0 d1 Y: L+ T9 c' j. v6 j
while ((values= its++))
/ D& q# G4 x5 w& s8 [' } {
$ F$ C3 h8 x0 j9 T& z& D9 U error= write_record(thd, table, &info, &update);
' } w/ f. d }8 @ }3 z4 Y' c$ {) u4 [$ S! v. J
}
: o2 {# f) G5 e9 y8 C' W//写入记录; D7 k8 b3 o0 i# I+ {* _6 R
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
% ~0 ^$ {- G( n: `$ c{/ ^/ C; r& _8 \9 P! c
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)& c0 ]) ~! K! n% a1 T. @8 e4 ~
{
3 }8 O8 y% V$ E1 ?2 H // ha_write_row 重点是这个函数
% O" v# T/ |$ k2 ~/ p% { while ((error=table->file->ha_write_row(table->record[0]))): _( x" v+ u& P3 h
{: q# A! F" X7 W2 R& u" `2 V
....
! {, u" t; j6 M# f+ d$ _$ S0 R+ a3 y }/ Q' |6 ?3 |. H1 x7 G" Q4 E3 R
}
7 ]4 [! f5 J* @( r; b! S `* {}0 l" V8 X! l6 X' F4 u3 k
" z' b* Z: A' T9 {- j+ [8 }) a
5 G1 o9 q9 q' d7 K
B0 b7 H1 ]9 h; A可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
" a) d# O* J. t; v* q& f& I( s s7 f j- s- d7 z
<3> 继续挖 ha_write_row
1 ?+ W; A: U/ { m- x6 `0 G
/ x( x4 x0 _% l2 Z, I5 B: ]/ @int handler::ha_write_row(uchar *buf)
3 w- F# ?9 w4 u{
- ^8 Z; O" j. F: l, I# A; {0 c MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
& M G- O! J7 b2 M: e}
" o6 f; w& b5 D* z% w: o9 H7 t/ [
//这是一个虚方法
2 U: z7 H! O( R' p' i6 E) xvirtual int write_row(uchar *buf __attribute__((unused)))
: |% ]7 j T }+ w; I3 K- v{* q9 V4 p6 _4 L* z; l
return HA_ERR_WRONG_COMMAND;
& V+ v& z1 j" _% ~7 k}" j# R2 E, M, z) ?. r* V ]) Y- v
6 z, S/ A# [1 H8 M
; f. |& O5 W, E$ U' ?
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
: u2 g+ Y- D; ?% R% F$ S7 J
. R1 N$ r( t: i& g0 T$ B" Q3. 调用链图3 i3 g" j/ f/ b4 w
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。. L6 \5 X0 F$ ?8 x! P+ z5 u9 r9 ]
![]()
4 ?; j- ~% y% c
3 O) R5 Q' k" d) B6 e6 D* v8 \% K7 ^
三:总结
9 ~2 j$ x& s' |" o5 k3 U3 }& K' K大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
' A4 h* D. \3 l————————————————
: a, {. H0 T/ d4 W* w版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。1 Q/ j) l" r, r7 W
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
+ y- Q3 G! O1 K; E/ B# ^ |
zan
|