- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55578 点
- 威望
- 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 田老师国赛冲刺课 |
一:背景
# m" A }8 A; k9 k1. 讲故事6 V4 u1 j+ e0 N
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
d6 w' U; t* _1 A6 @! d$ A: c
, D( R. n: X/ |1 U+ E0 k二:了解架构图
# S# \1 v2 F0 I) g5 w0 k; {0 e( Fmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
. }: g) k2 Y1 d8 a I. U- w9 x
2 y/ g2 ^$ A* h3 x; O1. 从架构图入手" x" e/ P+ `6 l* U* _0 G- j
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。4 P# Y+ \; `2 t2 n$ B
![]()
8 Z- s# l$ ]' i, a3 H3 W, v9 V3 M( k# K# G
+ N$ g; ^+ s2 u% t! g; s其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
1 \: A) H/ G" i( J, Q0 ?) A
0 C+ ]% B6 |4 i. A2. 功能点介绍
3 d) }5 t9 a5 B2 mMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
1 W% L& D: T- }; p" ^* ?8 A1 n& G( F6 b" R9 D9 K* j
<1> Client b2 Y0 y6 B8 O+ ^
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。) v( v. c; I8 l& N9 [, {4 B$ ?
( z7 C* m' O+ m2 b<2> Connection/Thread Pool1 \! @7 S3 ?$ u! `- W' ?0 ^, ~# @8 a
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
* b: q4 n3 ^0 I7 B9 o
7 G# C' E8 C) q' @5 Q4 h<3> SqlInterface,Parse,Optimizer,Cache
! c% K3 }& t/ g, {. n p对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
$ @6 m$ t* T! I5 K* E$ P; s p
" p( B+ R0 `$ R! A4 M) j<4> Storage Engines
/ h( c8 b/ W4 V9 ~9 |" ^8 C6 w负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。& k/ i8 ?, h# q% x" y2 M4 S
% f2 y& M+ q; i8 I7 N! v三: 源码分析
6 z( d! F, {7 L9 ]- y' F关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。3 G* n" R: [$ K1 @; V$ |
1 h# o5 M t% Q* ~0 D
1. 了解mysql是如何启动监听的: \, s1 s K9 O/ Y9 M8 Y( o
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。 d( ?, t/ s0 E m/ @) `
![]()
/ E% i5 S0 X a# _( {
' b) o, F, X( R) @& s
+ O1 Z7 v( x8 h8 b v1 Z6 u$ j从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
/ y T9 S0 r: k
. E% d4 D6 n& I$ ~; s6 I0 I2 K<1> mysqld_main 入口函数 => sql/main.cc
% `' j; W, u+ D: w1 j# O" Y* b7 W Y4 G! g; j
0 B3 w) x6 o- U
extern int mysqld_main(int argc, char **argv);
: \! a* K2 T& X! n! ^' e! }- Z& g+ x. l0 w5 M9 a1 \, H" I/ g( _
int main(int argc, char **argv)
. U! K2 d2 J0 r d( x$ _: O1 S$ D( B{
8 Z8 y+ M4 A Y6 m* z" n! @. @$ l' f return mysqld_main(argc, argv);
* R8 f' E" i- l3 h( [ n3 _0 ^}
! v: X( B& g7 G! B' q& L1 w$ k$ o- x" ]
. s& k% D4 j1 C- o3 P
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。- ^) Z9 |# F, I; O( ]& `& C3 W
9 t% [% X' r* J' R! }
<2> 创建监听 l' o6 l0 u# o( e+ A0 p# n) y f
4 ]7 _0 d) }9 P4 J4 O
' K [* [0 J, r% i& ?5 m; Eint mysqld_main(int argc, char **argv)# {6 x, W- [+ a1 s
{
/ I" e0 m4 `( I" H2 z% G //创建服务监听线程! {6 i# h& C1 F$ ^& y
handle_connections_sockets();4 I+ M! R, w7 d _+ G
}
9 U- E2 e4 y% X' U0 } a5 n. {/ }5 S3 e N1 D$ e, z5 G
void handle_connections_sockets()
/ G& j* F& T8 n* ?4 e( \{5 Z( t3 P! f+ a4 V
//监听连接
% I6 [6 Q( ]3 G) r/ [: q7 @$ Z" \3 Z new_sock= mysql_socket_accept(key_socket_client_connection, sock,
4 S! e; }1 i( X0 Q/ N7 J$ R+ e, u (struct sockaddr *)(&cAddr), &length);
# w5 U- z1 U: s9 D$ x+ V' n: |8 v" {! X: w9 E
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
2 x: e9 D7 J% a thd->security_ctx->set_host((char*) my_localhost);/ b4 T& O' t+ o; H
3 K( q5 \% z8 ~& ] M
//创建连接# O% X6 t9 D9 e+ Z
create_new_thread(thd);+ ?0 `8 W" A7 [0 U7 P8 s
}
/ q. z* w" y" A0 e! C1 S% G& Z1 m7 ?- A# j" h
//创建新线程处理处理用户连接: y3 a. M; B/ a3 z3 w
static void create_new_thread(THD *thd){
8 ^. e4 F+ B6 [- x" i; } \* t x( C: ^5 F' ^6 w% P
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;1 X# S) ?% P. q, Z1 t7 B" M& e
% e% g6 A! |; E7 Q
//线程进了线程调度器0 F4 [: u9 C7 D9 F7 _! r7 ? P
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
, c- r9 K, F9 A9 }& _}
. y4 ~2 l7 s8 x8 w; T
# ?% `4 K4 R+ m7 T- x& u3 }
! Z+ _; k7 q1 h% o }+ _+ B: y至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
5 z1 A' h- p* U- O1 v2 G, v# U# a) _2 S" v+ r! V8 h! {
* v* b1 V/ l7 r" x, Q2. 理解mysql是如何处理sql请求
0 L" S* T, ^5 x3 E" w. G这里我以Insert操作为例稍微解剖下处理流程:
8 @. ~, K) H6 u$ H2 @% B: W u5 h5 h2 f0 Q/ f: i
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。" b5 S: C. n+ E% E9 a9 W* P
/ M& q4 i- a) a; \! ?9 ^ s
: |# t: x* {& I5 m: g
static scheduler_functions one_thread_per_connection_scheduler_functions=
5 F6 I7 b: v& Z& ]% z' V, E7 G{; {3 e0 l! b$ a+ A9 V/ ? }
0, // max_threads" C/ _6 F& S3 ^% J
NULL, // init
( D1 x% L9 @0 F2 B$ s1 r$ e init_new_connection_handler_thread, // init_new_connection_thread4 ]9 Y$ p1 r: M3 D
create_thread_to_handle_connection, // add_connection
- a( o- T: ~0 y) k" q NULL, // thd_wait_begin
3 n, H. G9 E% X, m( ?7 P; C NULL, // thd_wait_end1 O0 L! z* A6 C9 E
NULL, // post_kill_notification
2 S# y0 t9 O6 ^0 | one_thread_per_connection_end, // end_thread
8 n, h2 y9 {5 v2 j) }& F4 b NULL, // end
% j( D4 \& j" }/ d" o( [- m};9 V# T2 D _! L
5 U) c& F% p+ [* V8 P/ q
. ~% a2 M. }3 u1 O8 g0 d! z4 q: H从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
" J2 p: K$ ~- b2 t( N2 R, }" M
: w: ~4 }8 J7 D: m% ~5 J5 Q<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
" v- B& j/ a3 f9 ?! u. B
# E& J+ R7 t: e: t9 \' Cvoid create_thread_to_handle_connection(THD *thd)
5 z7 D0 }" a2 o" U) [* Y{
- U# s8 T6 x, U! S3 s if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,( ]3 p; u: m9 ^
handle_one_connection,(void*) thd))){}7 n* Y/ I- U7 Z, B. d# p }
}
5 J2 B {3 e, d7 P0 A% o//触发回调函数 handle_one_connection' L& o9 Y5 i! e; S, A
pthread_handler_t handle_one_connection(void *arg)) Y4 i8 M, L: ~/ ?
{
" K$ K/ d- Q& b do_handle_one_connection(thd);0 B& E) f: y3 W
}
! @, K9 ^2 k) s' y/ J% X//继续处理( |/ E1 M* j, M, v3 l) a% ]9 R& {
void do_handle_one_connection(THD *thd_arg){
. m) N. ^' s" U/ J& q4 V while (thd_is_connection_alive(thd))9 L9 P/ K9 t1 w8 P
{
! Q1 r3 q& ~6 J7 Q; Y7 ?) R mysql_audit_release(thd);2 z1 b5 @$ @2 O( Q W
if (do_command(thd)) break; //这里的 do_command 继续处理4 l m+ v; Q$ u0 j' u( Z1 R/ R
}
3 a! |0 D' r- c5 w7 \; Z6 H% @- f* @. [}
; a- C0 Z& d( D3 C! m, v! s+ ~//继续分发% W. L* ?5 k. E7 [8 c- z0 s) H
bool do_command(THD *thd)
) S/ F( h- u3 V( H, j& v{& Z( M( D( `, d: K, r0 b; Y
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
) O6 U! t8 B, N% b$ L# C}% A/ v* P A( b$ F4 b
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)4 ^2 [! W% C* |- n$ ^
{4 |7 t+ \- x8 y
switch (command) {; i) m( m) a# e# B! ^' E6 ^
case COM_INIT_DB: .... break;
! A% G5 P" J5 Z/ V9 B .../ _. l3 d* u" |
case COM_QUERY: //查询语句: insert xxxx2 `3 x C7 F' }% q2 }
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
' e4 n4 B# k& L3 Q% I' ] break;
: y" g( B+ \ {. v+ ?2 w' ^ }
/ }4 A! U4 B; n5 p' o}' K. ]# |7 S0 z/ e" ? Z
//sql解析模块, F( y; d5 c5 \9 q0 V, B" b8 l
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
. l# {! `' `+ y8 [{; ^9 \1 e# K$ n7 U- m
error= mysql_execute_command(thd);
* H$ O1 k' p4 g+ r- k}0 C9 m0 H$ b% i6 F0 P0 Q @
: O! r9 P8 _. ^1 B' {6 i( g
: j; e8 H( t! F% c( m: _. H<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。1 w5 I" n* H) Y
5 L5 Z* {, A1 u) f8 @4 D//继续执行( H+ g$ m* V2 A1 g
int mysql_execute_command(THD *thd)
' \' O# k: ^4 p$ c; ~{" M8 A" r7 q3 q$ L9 I+ Q+ w
switch (lex->sql_command)
$ m' Z# J; Y3 Z" f, h' {: p4 s {4 A0 v. _3 M. V* Q5 u0 L
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
) q! B- B3 s1 Q. {" b. K
+ C* y1 G5 S/ N //这个 insert 就是我要追的
% n- o0 S; B; b2 | case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,, F5 R5 D1 Q* x8 ?7 o+ ^! x, p4 P
lex->update_list, lex->value_list,
) ~7 x8 C% B/ s. V lex->duplicates, lex->ignore);2 U9 X: j/ W$ x+ C9 u+ F
}
/ u! ^5 r6 C( X}5 u- Q9 l f# ]! O: V4 V
//insert插入操作处理
# }+ K; H% y1 U/ F8 c# d: C, q h, Abool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,6 q( o; }$ o1 g
List<Item> &update_fields, List<Item> &update_values, ; `" Y8 y3 l* D7 z3 x
enum_duplicates duplic, bool ignore)
) A# K/ p$ i3 l1 ]; U: s% ]% v{
7 f$ O2 _& r1 H1 b; v) b0 @1 w while ((values= its++))/ U# I+ c: F7 `! T
{
) E+ D" g4 w- S3 B+ l7 } error= write_record(thd, table, &info, &update);
6 I5 r& }3 l5 E3 T }2 E; v' w5 q* L: P
}1 W7 A2 D* l g q, m2 G: g2 G3 Q1 P
//写入记录
. R6 d% E1 ^, O. y Oint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
( _$ Y/ s v/ S9 K9 E/ v{
. r) P$ E9 _) l$ E2 ~. B if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
1 D9 W& ]# X1 a' t/ V; S {
3 l* M4 I( m |) ]$ Q7 J // ha_write_row 重点是这个函数
) D( b, D+ P' Y* i- a while ((error=table->file->ha_write_row(table->record[0])))8 U$ j j& v* R9 W
{
7 M7 Q" Y+ H; V# S* Z1 \1 @" Y ....4 \9 L B# J& i: E
}2 E: ?2 P8 k8 ^+ W
}
7 A6 x, M1 a1 x. F$ g+ w/ C} f: ?) R+ i& E4 z/ _
+ ]! S, j9 H, h" {4 h# ^2 U8 ~# o4 `& p+ b, ?- p
& o0 t) F0 @9 b' r7 W0 X G可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。" x5 [- [ w: R" z2 L
8 g$ y7 t$ n+ C$ ]5 H
<3> 继续挖 ha_write_row0 i' v, ~5 r& e4 h6 a
; I1 x+ \7 H# }* R
int handler::ha_write_row(uchar *buf). z- o' L: G/ N4 g' ^% x. s- g) U
{
8 C+ k! V. g3 z& @ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })8 n- n4 {5 ^, G
}
( P O+ j) p! p
1 a/ v# i2 g3 B( F+ o9 ^) s//这是一个虚方法
2 M5 d3 ~2 \4 _5 {) G# ~* mvirtual int write_row(uchar *buf __attribute__((unused)))
: ?4 v* y- P" ~! v0 B- L" q2 z& h{
5 O$ q) F. Y- V0 G$ ^ return HA_ERR_WRONG_COMMAND;8 a7 l2 b/ M! r% ^- Y' \
}
3 t$ ^% @7 c/ j" u0 S
: w9 Z! E' m: T3 G8 F+ l% c$ r. M" ]; ]
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁0 o& p$ b6 [1 }9 b
. Y2 _7 C3 n5 j- c6 p" M3. 调用链图
1 u" t- E7 k: ^9 r5 }0 ~+ X这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。* Q# n! Z9 ~& }, I
7 S' b2 d1 l2 i
) M: M' b1 p4 D2 v, Y
5 G8 @- w) l0 g, t- r1 `! A' \5 a三:总结
7 e5 g; j# O" O/ J' ~ u大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。; l% D# k+ {( U7 Z6 D9 n) _3 r
————————————————
& E# e u9 M# S( L' R9 u6 C4 B. `版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
+ J; E& m+ s; |/ p7 F, B& X原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
9 X0 x2 V! I2 u! t5 y- a/ E |
zan
|