- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55492 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17599
- 相册
- 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 田老师国赛冲刺课 |
一:背景4 K+ b: }. ]8 v' J
1. 讲故事
8 {: ?+ V- y# O( I( p0 o3 n# K最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。! W L; w& Y X4 F+ Q# ~ g7 a
$ h( G) U# D' h* g二:了解架构图: K( X* |) k8 H# x2 ^: |6 E
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
% k) g7 }2 }/ }" N( ]! P9 U
( ?9 T4 P) ?- z1. 从架构图入手
- ]/ N9 g$ k* {7 h8 R6 B大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
: d! g1 S/ a! o# D2 Z![]()
3 S! @* y% L% F x& n& y N# {& s$ g' s+ b# {, Q- V
! H1 A: ]7 ]7 Y, g( C3 [0 P
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~9 x! Y2 q/ q9 ]1 n1 |
! j1 `, G7 E# m: f0 @. ]! D
2. 功能点介绍
9 w( h$ C; P e$ M% r% {: [MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
0 V( Y3 Y y4 d/ ]; _; E
. U' K& ^. I5 A<1> Client
2 ] u: l8 F* b, X% f* G9 Y5 X不同语言的sdk遵守mysql协议就可以与mysqld进行互通。7 \, A% h& S$ s, b$ G0 d8 m
. W% a) F' c3 G2 e
<2> Connection/Thread Pool
. B& l8 s9 Z+ a. [( qMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
; x# R$ y0 E) H, |; r1 p6 Q) {4 q' T. |3 o2 N @
<3> SqlInterface,Parse,Optimizer,Cache* ?0 n9 }" t4 C R
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。4 L" i; w9 r% r. I. U0 v
3 g; a; Y; i7 J0 b- e6 q
<4> Storage Engines* J8 ^" T8 E/ L8 R* s
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。4 ]5 o& S! u; m U! M
$ L1 b& ]- F6 t0 C- Q: }三: 源码分析
- W3 m% t: J' B& B$ `! t关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
. a4 [5 i q; f
, Q- m; Q$ ]' a& z, B' @1. 了解mysql是如何启动监听的3 H, k! ^ Y5 a3 H: T4 P( D. H
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。. p0 U( Q. T/ e
![]()
" q2 {7 p) ^0 s+ H! ~2 j* C" s6 w4 k. g4 I' o& k
* L8 \$ Y1 _& F, a) {
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
+ C( v7 g5 d) C/ @* Z6 I
4 N* d/ H p7 {& ^<1> mysqld_main 入口函数 => sql/main.cc
, h' a" M$ }! w% N# A2 ]# c7 O, n8 w: p+ g
4 x& u2 o3 `/ u. q8 G/ m" M
extern int mysqld_main(int argc, char **argv);
2 f% S8 y7 c7 }( E; A5 c; K/ u0 }4 `" o# z: c( ^
int main(int argc, char **argv)
; V" F% z6 Y* S+ o{; ?3 Y, g7 R; J0 E- U" s$ T
return mysqld_main(argc, argv);
4 |2 H( w( I$ L' |* O- z8 i# F}
4 h/ s# _& \5 V) w! G: N
2 ], U2 G9 w( s8 h- r
E$ A6 V5 e7 D K9 c这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
3 S- q, @& A' z! ?1 C" G- ^" \: A
' B( @2 l: A$ U, g<2> 创建监听! i$ m; i" F+ ^$ L+ b
- p" R/ O0 x3 M, F* J; L
1 m* ?) `3 q4 }3 Qint mysqld_main(int argc, char **argv). }4 }( ?/ v# `% Y
{
/ F x: w& y! v$ ?. \$ h% N) l //创建服务监听线程
% M, f$ L/ J% m handle_connections_sockets();
+ u( } j* F# Y3 a% G7 Z, |}
6 x* R9 S! y$ @5 W' E/ R; s1 G; E, F
void handle_connections_sockets(); l9 G- U; z/ r
{2 _- J$ J2 Y c
//监听连接( @5 P/ l/ R, w; U/ w6 r
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
4 _9 b' B3 w8 b+ S4 Q' i* w4 j (struct sockaddr *)(&cAddr), &length);9 C0 [! i4 f$ E
' u& M7 R0 b1 b
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))" e X& l4 C+ z- Q
thd->security_ctx->set_host((char*) my_localhost);; \, K- T$ U: c) o, N3 g' e
S, C- `3 ~- s, T( q //创建连接
( n/ L! ]" K4 o# s# M1 _ create_new_thread(thd);
7 {- t" Y- C$ `}: q2 w+ t. D- X" ?. G5 A
/ k! Y- h- O% p ]; \; ~//创建新线程处理处理用户连接
2 f! W: f2 o% y( ustatic void create_new_thread(THD *thd){
* p. |' I% ] }# f1 Z9 V4 t
6 F `/ w5 k" B& } thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
: y' ]9 S# n$ ?7 t# ^ }) y4 a
$ p9 M( C' y# j' }( m Q: Y //线程进了线程调度器
2 n. S' ] a3 d# ` MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); ) C6 `' ?) A. Z) I$ r
}( P- C5 b; W( d, A" U8 H7 _
W7 C/ X& k% U% R6 r; a+ T+ j
- D5 t& E3 c& B# Y
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。2 |- V: N! e# r9 ^4 V3 W
& a/ K2 Y+ ]: Q3 O% h1 M' s8 X; j
2 w% d+ D$ f8 q! y8 M% J2. 理解mysql是如何处理sql请求$ a: z4 ` v3 c0 X7 n
这里我以Insert操作为例稍微解剖下处理流程:4 r0 w9 b. [4 p( ^/ n
7 Q9 t5 V: o0 ]6 i当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
" o. K& C% n- j$ n8 {- s# r
# G: n A. n' k& N. h3 s) q' }7 c9 J0 ~* z" K2 O
static scheduler_functions one_thread_per_connection_scheduler_functions=$ ^, e- }- n y& G0 q( y
{& e9 c# H; P* i) o7 d
0, // max_threads
, ?: v: q, I- D1 x5 G9 I NULL, // init
* b3 A4 t- m7 s' a& t1 { init_new_connection_handler_thread, // init_new_connection_thread' ~4 ?% y4 @/ m9 |7 u
create_thread_to_handle_connection, // add_connection
6 z* @2 x' J3 y6 F$ ` E NULL, // thd_wait_begin( l# K% T0 w4 c. ]
NULL, // thd_wait_end
; D, M' x* c5 V+ @" E NULL, // post_kill_notification% J9 I" J7 j8 J8 v- t
one_thread_per_connection_end, // end_thread
3 L+ u" G' N$ f) o, l) |4 Q NULL, // end
2 _" i2 M4 g* Q, A# l};- ?( W, C; ~7 g6 F
6 U$ m1 j. e5 F' u3 I; k1 d
/ r$ v! ^/ v0 E, {! V# @
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。& I. Z0 t3 W. [. m* _/ _7 j
8 O: O g* C+ h z$ D<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪( W* J) {6 @( V1 k
! z( D4 f9 P$ E( o6 f. fvoid create_thread_to_handle_connection(THD *thd)
3 z6 z- x7 Y; d- ^3 J6 [{
& b7 k Z$ y0 q, w/ O if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,2 F: p; q3 a( k: y c
handle_one_connection,(void*) thd))){}* r* l5 P$ f# S, T* ~) }
}
4 ^, Y+ z, N* @' z//触发回调函数 handle_one_connection
( {8 `9 L6 y9 }& p5 q+ x1 i) kpthread_handler_t handle_one_connection(void *arg)
2 W- T$ r/ E/ L0 {8 A5 L{
4 n. G& f' ]' n0 I do_handle_one_connection(thd);: F4 g( t' h6 T2 t% O
}
c' F. f. S7 n& M4 j2 V2 r- s& _( l//继续处理" l& `( J6 f/ P7 K2 N
void do_handle_one_connection(THD *thd_arg){# S& E. S4 m9 N) S5 A' i
while (thd_is_connection_alive(thd))# Y. \* q7 Q" ~( ?- C
{# |5 L- D5 z: b3 y3 F
mysql_audit_release(thd);; O8 E6 g4 [- \7 P4 p/ y
if (do_command(thd)) break; //这里的 do_command 继续处理0 s8 m. a. s3 B6 [2 {9 \# q
}
2 g6 {+ G7 J( f7 H}( u: G% L' n2 p0 A% r9 Q2 j5 B* _
//继续分发2 k% K. j# i, }2 i
bool do_command(THD *thd)
+ M( q& z& z1 Y# x{( e0 k& ~! N( F7 P
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
9 I& T* Z& k, L+ |}
+ w; _; l u5 j! Zbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length). [5 P+ t, i+ N3 ?2 L5 S9 C
{
" W) ~; k4 [8 W% _: y switch (command) {
- N9 e. o! s. b7 u case COM_INIT_DB: .... break;1 [$ k" a+ `/ \) M+ k7 m9 D
...5 x- _6 `; o) N U+ P
case COM_QUERY: //查询语句: insert xxxx( \* E) W6 Q# Q) A& `) ^) k! R1 K% ?
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析0 E" f2 J, U; K: n$ a
break;
% b; P4 f+ F @7 G" M" K/ w }: n4 ]8 \7 p% ~
}
& } ~3 v- N0 Z$ v* U5 l' A a//sql解析模块' B4 S# c! ]4 ~' [
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)9 e: X- ~; U+ \4 t7 \/ _
{
V- V6 u. ]* ~. C# z8 R; f error= mysql_execute_command(thd);, q& j: H& W$ {. S( P" W
}
- x) e& K) A' \0 ?: a0 O0 Y+ ?8 {0 n! n/ }
! b/ y3 u, N8 W0 M# ?
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
5 P9 O. |( D% G# |8 Q2 e0 M+ \% o, R/ h t7 L" O& n
//继续执行! ~$ C0 V" ?5 d: X& A! e. \# s. d1 p# x
int mysql_execute_command(THD *thd): ?! r! l* Q5 |) T, f: ^
{
7 `, J7 x7 ~( _" M9 O5 }# Y+ n switch (lex->sql_command) ; m. k6 d8 D1 i4 S! s
{
6 G" h8 _- W7 n6 i case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;0 n4 @% k9 C* a$ f2 P" e/ D* u0 ^
3 w" R, D0 S+ a# }2 C7 a ?* E //这个 insert 就是我要追的" c U9 s! E1 C2 q1 y" B+ J9 L
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,! s5 }4 q' e, p; l- O. b
lex->update_list, lex->value_list,7 I) U. n1 c) |
lex->duplicates, lex->ignore);
' d9 `% C3 i* _ }
% b" F# l; M3 x/ Y}& E6 x' s8 K/ V- I) `7 ?& T6 o- A
//insert插入操作处理
( ?4 B) \6 \% q% o& |& dbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
6 O+ V8 ?4 t7 o- t3 h4 s List<Item> &update_fields, List<Item> &update_values, ' F: K% o! U- Y7 I$ ~' j K
enum_duplicates duplic, bool ignore): I% W5 h D( l4 s: |, R
{5 ?5 s, r B+ I* Z% N* ~
while ((values= its++))% m0 k3 A0 n' v. s& u
{, L, I( x$ Q$ b" j9 j0 @. c
error= write_record(thd, table, &info, &update);8 ~$ J/ N- e0 b% ~/ t; u
}
) g+ S: c/ L" B}
1 P! O1 ^: Q: d6 l% N//写入记录
; S% M$ Q8 j$ V6 P/ }0 aint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
M! a! i7 ]0 \+ }0 t& [3 [{. u6 {" m: i/ H, I9 }3 r' G' L
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE); z4 u/ k; ^, D" Y* ~
{
1 _) U/ U, Q! D4 c9 f B. W // ha_write_row 重点是这个函数
/ w) q7 z1 q% R6 n a. I+ U. Q while ((error=table->file->ha_write_row(table->record[0])))
# f$ c7 \; W6 Q* |9 W {
. H* y$ R2 d$ ] ]9 G0 x* q ....) U, z3 S% Q* ^* a' `! z
}) [- j o: t5 R- B
}
1 ^9 q0 t; R5 ?; c1 ?}9 v+ X4 E* D4 j7 B
. x7 A. l8 B. r$ J. B" l, @. w7 _& o) I/ Z
/ h* o6 b. Z2 c* H" C: K! W可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。. F* Q+ Z* i; J2 L: {. Y
) f) A# Q# v7 H- m# [* ]
<3> 继续挖 ha_write_row8 M+ B, t4 z# y
2 d2 ^* Y0 ~2 s& g' `
int handler::ha_write_row(uchar *buf)
/ [7 r5 Y& f v. y" h1 w9 J{
0 _/ y6 A/ }: z* h MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
3 D6 @* \( _/ z) w}5 I( [6 s2 A! i
3 Q2 G* Y! k% m. K
//这是一个虚方法3 S8 G2 Z: T! ?8 ?9 J F9 s, m5 W5 v% g
virtual int write_row(uchar *buf __attribute__((unused)))/ @! v7 `6 O" Y* `! N" _. q
{; i4 X: P% X2 `* Y; [
return HA_ERR_WRONG_COMMAND;
# A* Z3 {3 ^: N, G3 e}7 z O( u |: A9 W& | a) S
- f! ?. Q H& q- m
8 A8 D; c3 p( G% a& }看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
) |) H6 o' S/ N" M' z4 [9 ~6 r0 [$ i& `8 u- }, j6 @ |
3. 调用链图( ]" r4 }: } K* y* v
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。% J: ^* M. U! j" x: m
" o" H# S- R2 ~3 k( M
0 [; J! d& A1 R2 p/ j, {8 E/ A
& h+ k6 S8 a+ u; o, W三:总结
# q( n' o i- s& d3 L大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) k- K$ |5 h- T9 B
————————————————$ G4 d" b+ |) ~8 K' W
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。$ O& t9 f6 ~) G6 t6 e5 n i
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
x! Y% d+ O7 f9 s* ^+ _: G |
zan
|