- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55453 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17587
- 相册
- 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 田老师国赛冲刺课 |
一:背景
9 R7 j% I7 A+ n% P9 ?" y1. 讲故事
W2 p: o) K) D s' i% C最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。& ]4 x- U1 G, f( {3 L
7 [8 e. G3 E$ X
二:了解架构图
+ e$ P" y; V% X/ P5 U8 Q- Fmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。, U4 S. b/ I2 E$ T5 x/ d( |: v
9 g6 x4 R' `' @, ^3 U/ |
1. 从架构图入手
1 j3 h7 u, l- y2 k2 N1 r& a大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
& z* W. }% r& @" s
i. D1 k9 R" T9 m% l w8 j
. b* N8 T; w3 v2 L
1 A v2 I) o1 }5 F7 `, J3 U" Z+ m G& }4 V其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~2 D1 A# h" m: N3 Q. G; [
# D4 m4 R1 P$ ^
2. 功能点介绍
/ Z9 Q2 L3 x- h# o: RMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
2 Q& ^$ y( \# K7 S: S
* {9 p! b5 d! ?5 G5 Z<1> Client
+ L) |* ~$ `6 y! f$ `( w# q% Y不同语言的sdk遵守mysql协议就可以与mysqld进行互通。) S2 T$ S$ U* c% D" F7 X6 }
' Z* }$ Q" z7 n: P" M9 d<2> Connection/Thread Pool t% P# \6 g7 t" ?1 }* e
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。6 I# Z% s8 O+ K
, X1 x# r" I1 n- v2 f3 t7 r" _% d
<3> SqlInterface,Parse,Optimizer,Cache
* W1 o9 X Q5 P6 @对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
% b* T) i* Y( Y/ _5 m2 o; V# z+ d0 i9 j$ ]& P
<4> Storage Engines
5 ]& T9 s, V) I9 ]+ p负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
9 x5 U8 J; M3 @5 Z+ W% j* l5 l- a3 O8 ^4 R, G
三: 源码分析
Z% r& ~. M7 x& e0 Y7 n( h, L关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。' S% W7 G$ T$ J% v4 ^+ E# I2 W3 z% n
5 r. @2 B5 S. x# N
1. 了解mysql是如何启动监听的
, _' `& Z, d0 u. g; i& a& d手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
; T7 M5 {. C3 Z# |- u3 P( y& l& i( z4 b; t( R6 {
1 v( W# m9 o8 p6 s$ d, L; {& \+ y; u0 h- Y, _+ z! Z! _$ {6 r- H
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
G/ i4 C; M" j1 S/ s4 m0 e! W$ t5 v- e* Y6 D! @: C8 i
<1> mysqld_main 入口函数 => sql/main.cc
& [' w# m6 Q t# ` q; C' o# ]& @. [- e! a4 m
; u- J2 ?2 O: s! o) m7 i$ g: s
extern int mysqld_main(int argc, char **argv);5 I9 y. L4 {1 v0 O* n8 Q
+ e s7 K5 j- l8 r- t6 d/ X& u1 c6 D
int main(int argc, char **argv)
0 y' ]0 g# k+ W4 z0 n% j{
7 F! w" [- ?& n' @* v! H4 ~ return mysqld_main(argc, argv);
5 K8 f% L% S/ w- b! b5 W2 z% @}' Z1 D! ~* W, O0 q. H
1 v5 K" `/ \% R9 D) C% G1 @' K C
! D) I7 c: W/ K+ x
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。% u1 A" I( B |' V3 K
5 X; G3 ^. G: X6 v& W- i9 I1 J
<2> 创建监听" b6 N8 m: s5 I0 l! _" H; s
+ ?3 F8 C5 f/ v1 H* X7 o6 Z; s
; c5 ]: ?' I- a! D" a i8 a Mint mysqld_main(int argc, char **argv) v' s. X3 y3 G& {) ]: `& }) {$ R
{
+ m+ Z5 q. M D" M& C) O //创建服务监听线程6 O# u1 _1 j. `
handle_connections_sockets();' ~/ I. H, `; c7 ^
}# P, C$ K( f% x
* |- V9 p: g6 K7 b( e3 X! q
void handle_connections_sockets()
0 }8 x+ r* J( p( P{
, Z/ q, O- G" j' T9 j* `) F //监听连接/ H1 S1 D3 z0 R4 P& W' B+ E+ ^
new_sock= mysql_socket_accept(key_socket_client_connection, sock,8 E6 R; ~- s' C5 H4 F6 t
(struct sockaddr *)(&cAddr), &length);, O# i+ _3 r$ j, M0 J
( _" F/ p. k1 |" G! x
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))4 u$ c" @ ~; S0 I; @3 T+ @; E" Z
thd->security_ctx->set_host((char*) my_localhost);- W" O8 R- \0 q
( p% I: m) S( B& j- o8 o //创建连接
[8 ~3 T9 H# m7 G create_new_thread(thd);& C( \2 Y% Q. p9 S/ @. J$ i8 F
}0 s2 S; a, v5 v8 g4 E" ^- J4 I
6 o0 {8 m" Z( S9 D& e//创建新线程处理处理用户连接' I5 Y1 o# Z& F: ]
static void create_new_thread(THD *thd){% n: Q! r' v9 h2 n
0 n/ C) ~+ E( ~; y, l) Z# s! c0 r+ y0 m thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;* h4 `! }* B: l0 M7 A5 J6 h
/ u% o4 k7 ]+ t6 J$ E
//线程进了线程调度器3 _" ~" y: r/ g2 \, F( y3 Q
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); " F: ]& r0 X, O6 f4 }+ C4 d
}
* w4 p" [6 o, b9 S" j1 o# B9 A' E) p' P! v" S7 F! G
6 F3 {6 l8 D" R2 k1 R/ e7 F
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
4 c z( `' z" M2 X) d) \! a! X7 U) u
6 f* N' ~/ f7 ^, i2. 理解mysql是如何处理sql请求. n$ X. A9 Y" \" ]
这里我以Insert操作为例稍微解剖下处理流程:
9 ?2 k8 j* N$ V$ V+ F1 \
7 U& L) A6 Q) L: a当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
2 {7 m# H: K. @+ e
3 E) F) P* G! R" I. A9 R
( M$ i" e' a. m. N' Pstatic scheduler_functions one_thread_per_connection_scheduler_functions=8 }& [3 }" ]- e! t0 J! h& t, a: w4 }
{
% T# B: x, g% ?" r C 0, // max_threads1 H0 l. d$ H9 r9 H! K
NULL, // init& _8 h: J% M3 Z3 D7 P: E7 ^
init_new_connection_handler_thread, // init_new_connection_thread8 J" w, g# B; l( ^- J
create_thread_to_handle_connection, // add_connection
, s1 t* B/ z. O) ^/ o3 _1 ? NULL, // thd_wait_begin
/ G5 m3 w4 ]& j5 A NULL, // thd_wait_end
3 ^6 Y5 ]7 j4 ~/ w1 K NULL, // post_kill_notification
n3 ~0 z0 f8 o- {; V one_thread_per_connection_end, // end_thread
8 _2 e6 p. }' a. E* h0 @" R NULL, // end8 o9 Z- m6 ^$ A% Y
};6 {9 g9 l7 P" f
7 ]4 K) R/ r8 K, }9 }) q0 ?: b; G7 I. O$ Y, j
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
, M% y, {8 l8 S# `6 V
/ u" ?9 B& m( h: d: K) z<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
+ e( K" a( i; O5 ^/ _- H! h% _# B3 v/ i8 q5 p. p
void create_thread_to_handle_connection(THD *thd)
3 o" R: U ~# @* k9 O. E* Q$ Y$ P% l{
1 Y5 I7 N5 c3 d) l if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,, V3 Y1 h- X6 Z- [* N; {: h
handle_one_connection,(void*) thd))){}
7 X) N' B+ c W8 U8 M$ x' I}
$ M3 Z+ j- h- ?' D//触发回调函数 handle_one_connection
& j. m% s$ w" X3 Y' w5 L- [ `pthread_handler_t handle_one_connection(void *arg)
3 {# T! L( O+ C% x) r' D- ]$ M+ A{; d0 n( ]) @! I; [
do_handle_one_connection(thd);* M8 D( p, I, c( c; S
}) {; U' h( c6 m w5 M; K8 E
//继续处理2 s0 z# V8 E/ x
void do_handle_one_connection(THD *thd_arg){2 m, T- v2 y D
while (thd_is_connection_alive(thd)): |7 G7 A" f( }1 P: W- D9 V' a8 r- \* ~
{
0 Q6 {9 j! e. y! K) W" B mysql_audit_release(thd);
6 r [5 |6 X% s4 S j. V/ K if (do_command(thd)) break; //这里的 do_command 继续处理3 c* \/ ], z' e* C7 g
}+ ~$ n( `* r2 h2 F' Y
}
/ H7 Q6 B! `# I/ w//继续分发4 _9 s9 E7 o1 M* D
bool do_command(THD *thd)' m0 C* P2 y* F3 K+ b2 L
{
* x* O; E; e4 H$ o return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
' b6 S; G$ E5 r2 G}4 N1 f& n) |' X
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)! R( w5 Q6 P, j
{9 g% J7 D7 c7 o. p! ~
switch (command) {0 K1 H! d% d5 H/ [$ V; a
case COM_INIT_DB: .... break;
9 Y A5 m9 d- T& d7 R) A ... `0 |0 P+ w2 F+ k7 N4 i
case COM_QUERY: //查询语句: insert xxxx$ a! r' s) b8 L- c
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析, s/ r9 B6 W, j C* H
break;& d4 P& i+ Q0 S' Z
}
% [; C6 w0 ~. j$ }# r( n* k: s% C}
% ]' Z# u( G8 _//sql解析模块
! R' P) s" X$ x3 m( ivoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)6 e" u/ d: K# X i& R' Y! _! I
{
; f3 n5 o% _! P: O error= mysql_execute_command(thd);4 N# n4 D m5 B" Q" T
}1 D( z( u: m' W
- }- b+ A7 A4 y; g& {+ ~' U) U7 y8 o2 t/ ?. }
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。. g. z$ O( j7 O! Y( s
, q: K' Q% B/ l7 H8 n3 R4 v# {//继续执行
+ ]9 @' w% j8 @4 u4 R! W. Fint mysql_execute_command(THD *thd)/ [& k4 K: x1 @% d% ^9 Y/ i
{0 T, q) Z' |6 o- k. v7 s
switch (lex->sql_command) * q% w- n. X! C$ C# u
{4 c- r+ D9 f& J3 ^% ~/ l: G
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;) N) D$ t0 q* Z3 W |
8 K- P8 W8 ?3 \, X( V! S //这个 insert 就是我要追的
& c! i' _% O* q. B# g. m! I case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
: k1 [4 P+ P1 { ]! b ]; _9 T lex->update_list, lex->value_list,
- E: k* Z3 U) Q+ ?) A2 t( S lex->duplicates, lex->ignore);, a% V. |* m0 e% K2 w% o2 a
}
2 L5 e1 J3 `+ b) D}, Q5 n$ S' k: o: k2 ^$ V' _) R
//insert插入操作处理
& o5 N" v# G; Dbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
, Q" H/ A9 g4 q7 l# u; K List<Item> &update_fields, List<Item> &update_values, ) s' N1 l; Z" y, L0 G
enum_duplicates duplic, bool ignore)
1 f) _6 e5 P% ~8 A+ l/ N{! _$ E/ h5 T- C" V1 M4 b; o
while ((values= its++))
% I( U8 Y8 ]0 ~2 V/ x1 f {8 I; x: e, D1 A/ D
error= write_record(thd, table, &info, &update);8 `" \" [+ W z5 @
}
$ p# ]" C2 i/ i5 ]+ b( D0 X}. ^4 v0 H* I5 F1 J% h* S# Q
//写入记录& s; ]- d9 I8 O$ q) C# t
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)6 N/ t( P6 O+ U4 ]
{8 ^* S9 R; P' s2 R
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
' V; p$ O5 v; N! |! g! n" N {7 }! E( t, x+ ~" J
// ha_write_row 重点是这个函数3 s5 A8 b# d/ K+ {; X
while ((error=table->file->ha_write_row(table->record[0])))1 _0 }3 c8 ?1 e+ M9 i1 I& C
{; i {+ r8 J6 I' W
....
2 A% t- a& }% [ }
3 r8 P$ e9 y+ f2 l# q }
) T; k( n: ~! s& q}! Z# V: H7 o M; W2 N0 ]
! n: x/ k- I. M! t% ?+ o
! n. p' v! V9 T" W; u3 g0 d3 Q9 i& q
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
; P3 L! w8 C! k+ g; I
$ ^1 D- ^5 [* E0 t<3> 继续挖 ha_write_row# J D( W" \0 k, l9 d+ h
! E- r6 t" }, P! G* ?int handler::ha_write_row(uchar *buf)
1 Q+ Z' ?. c0 H4 X$ W{
2 r# h U' t: ]2 E/ A MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })( o3 f: u& f, i) e$ G% W) V& B
}
; u7 C x- G$ C" `8 t u& @+ \3 i* B4 W; [: M6 _# j% G
//这是一个虚方法
0 f T/ Z% d ^& ]virtual int write_row(uchar *buf __attribute__((unused)))
% K' e" @4 k$ F) Z; n. M+ X{
# G$ w% }% U. h$ I8 f return HA_ERR_WRONG_COMMAND;6 @: u/ X* G1 J
}& p' W0 m7 E2 c3 b2 S3 G
0 q: F$ U" y- y2 _
% C0 [9 t1 n8 ^& J" r/ O看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁* u# O9 R5 y0 ?7 s
' A4 Z1 r3 b$ N* F5 g: D
3. 调用链图
( X5 ^1 w3 E" |1 l- \& s2 H这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。1 I* c7 [" J5 v P' P
6 v: f2 h& O. j" I' |! {8 z" s$ i
( s& {4 L- R0 n7 F5 `2 z
, e8 J7 |7 ?5 `1 I: F$ E
三:总结: `" b# O0 h) M" A
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。6 d$ `3 k& }# G% _" q
————————————————
" U, u7 d( R5 J- S* Q4 T版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
; ^! g) U" k2 t' }原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415' l6 A7 s5 U! \+ u) W7 a
|
zan
|