- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55512 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17605
- 相册
- 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 田老师国赛冲刺课 |
一:背景. }( \' v9 T8 q+ I
1. 讲故事5 ^! s! D% `5 y( \! h0 A+ c
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。, E2 X- [& k) i8 |3 @' o# F
( h, J( [( D1 L* f& q) b+ N& O二:了解架构图
9 v0 J* w- ?& O" D. Q: t' ?mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
, g+ r6 D/ T9 d) ^+ d9 T# p. k, W/ z+ p
1. 从架构图入手. |- G# A" }4 T* k P9 j4 w
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。3 X0 `+ e- v% g* ^+ @. K
4 m0 s, n. G1 T+ f; a, s; b
8 ?- F% [: Y' I4 X9 w9 ]; V) g7 X3 j$ ]; r& r9 B k
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
: `$ c' G) x" |- @) U, {) e1 r% R2 \, q! |6 {7 P* m8 m
2. 功能点介绍4 z- @ D8 D5 ~4 Q
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
7 o; `* J5 N6 B7 S1 e9 n0 Q+ Q( N+ F' X9 _
<1> Client
9 S/ `& I" l0 `不同语言的sdk遵守mysql协议就可以与mysqld进行互通。) c9 G+ o# Z" j9 f
5 V7 y. t1 H0 Q; l, F9 L<2> Connection/Thread Pool2 x; G4 k l% ~4 {- G4 I( v/ G$ Z% `' y
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。& r! D0 e, D7 A8 K0 D. p
; l% r9 D0 N8 i5 r9 G<3> SqlInterface,Parse,Optimizer,Cache$ E3 D) v1 O# v& e9 [
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
; U1 N/ V/ }# u! r7 `4 B3 R! f7 o) |2 B; C
6 `* [2 R. c) T( ]- X<4> Storage Engines
' J+ | V# ]. v Z' c5 O负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。0 V1 M' O7 ^' m' z2 s+ ^" l- B
/ z( |: u5 K( M! O1 k7 E三: 源码分析. M+ U+ ^; s- a' G6 v1 J; l+ @
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。& @8 p, ]; [6 [/ D
; |- N/ H. J3 Y N. u7 o
1. 了解mysql是如何启动监听的7 ]4 A% q+ Q: z% m
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
0 ]2 v5 r& [6 T+ q0 h4 u6 q![]()
, i5 Z% m5 B& ]2 L9 N$ A+ n' y0 V8 d1 Q; Q8 h0 i7 P
, S- {% Q- @. `; @
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
0 @. `) o+ D1 K/ n2 p. m
& M' a9 R4 w4 |; K5 Q<1> mysqld_main 入口函数 => sql/main.cc3 g* T: Y' N9 a# ^7 s% `+ L
' R/ g+ l# U- ?6 e3 {
3 {" A' k' }$ Y# q- I# w7 P' i7 Kextern int mysqld_main(int argc, char **argv);
# X$ P: P! W; u
3 C0 B" i# O: T4 q0 zint main(int argc, char **argv): b9 [0 x4 z/ [0 l2 u
{. d1 S( f: D; `
return mysqld_main(argc, argv);
8 v" _; d7 W3 h8 L0 k r! d9 B9 b9 s}
; ], W, r J, j0 @- J; Q E& |' V8 u
1 ^# _/ i* t1 e3 y1 P1 q这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。( G9 v- _$ w) z. K0 r) D2 T
! G9 j- D( W1 |$ h9 v# _
<2> 创建监听 I3 O* y$ U4 {
9 y ?, W6 B, O! u5 [! ~7 T# p: G9 ^9 `) O2 e2 C& @
int mysqld_main(int argc, char **argv)
" o! C6 U$ l% o) P: i) L{9 e# y7 ^' T4 b+ z
//创建服务监听线程
3 P6 ^2 A& s9 ~6 w' E4 | handle_connections_sockets();
& w7 V5 C1 Z! M/ f" K}
/ [ ]6 C& U9 r. Z' @6 q4 r) ?3 T$ i. s' O7 f
void handle_connections_sockets()
; l0 i$ a; a! D+ h3 m{
5 u8 x9 P; @1 O# j. i //监听连接
* g& O4 Y! J6 x" ? new_sock= mysql_socket_accept(key_socket_client_connection, sock,
5 N6 F9 i0 a5 z3 l (struct sockaddr *)(&cAddr), &length);2 y. T% N$ ]6 C7 Q, u4 n z! y
1 W& v, S: ?+ U0 m/ k6 c8 c if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))6 V. ~0 l/ p3 E- i' ?6 C2 u, c$ J
thd->security_ctx->set_host((char*) my_localhost);/ q1 J( Z5 q5 f, z# v9 k. d. x
- g+ `" z/ t: ]
//创建连接
. t& t( `, s& ^$ O& o. ^! ^ create_new_thread(thd);
& v1 N" B. C2 B0 n( E5 C}' X% _( q3 Z' }" u! E
2 E7 C: n+ t& a8 J+ {/ N
//创建新线程处理处理用户连接2 G$ F* b" E: U2 `+ ~! Z
static void create_new_thread(THD *thd){
: A5 e! y9 m4 A- m0 n
# l9 s8 J2 v( a) {# M# } thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;$ W. j7 g9 B) \ z9 C. N& d
) v0 C8 t8 B. D7 m( t7 w
//线程进了线程调度器$ M; y0 A- v! S+ }( e4 {% Q) j# S
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
( e) W0 X f ~$ e}$ d# Z) B( P% Q4 H
5 v2 ~4 F4 `7 d+ e" T7 b G7 Z6 `4 W# Q" Y* V/ q
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。9 S$ _% h3 J- K& q
" H4 C7 N6 ?0 p
% C' A; @1 I5 p7 U: D! D
2. 理解mysql是如何处理sql请求4 y3 W2 z. w9 c p: }
这里我以Insert操作为例稍微解剖下处理流程:) K) {# [7 @' J1 R4 q" m1 m- a4 b
3 l0 W9 g$ I- q3 |$ V2 E当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
a* j6 T9 E; P2 z4 t: U7 @# D. L7 k: h, Y! S
$ Z8 K5 |- B8 f1 y8 \3 Tstatic scheduler_functions one_thread_per_connection_scheduler_functions=
8 x' Q7 `4 N1 S$ S- p{/ s6 k" A' `/ _0 `4 s4 ]# [) u% Q6 X
0, // max_threads' s, R, E1 g9 ~; y6 j+ ~: x' u- W7 H
NULL, // init
# {# w/ {6 R+ {. o% E7 b3 G init_new_connection_handler_thread, // init_new_connection_thread
: W( _) Q) P# V* p create_thread_to_handle_connection, // add_connection
; C, v5 Y- q) q1 ]* C( K E. T NULL, // thd_wait_begin
' x7 D" Z, [# m NULL, // thd_wait_end$ k) u5 s1 J$ X% b [
NULL, // post_kill_notification. A k2 c5 K# h
one_thread_per_connection_end, // end_thread
( x, [3 b0 E2 `9 x9 p J NULL, // end
! R4 F' S. u$ p: w};
# K' V7 v" ?% U% y j# E$ r B( L, @2 K/ g! W; _2 d2 G7 ~4 L
+ l, T! M+ A* E8 [4 \从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。9 ^# H! _4 q7 h" w! T
0 f* Y( }% v* ]
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
& n0 t" R5 s h4 \- \3 s: _4 H0 [# e, g( y2 M" k" J
void create_thread_to_handle_connection(THD *thd)1 _! C- ~3 ]) G- Q& R O, h
{3 a( t7 t1 v$ u/ e. B4 C* p. i
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
+ }: n, [. [1 c handle_one_connection,(void*) thd))){}
/ \2 U b$ K# w8 m" P1 D}
( V @$ y1 @4 Q& k# O) X5 L//触发回调函数 handle_one_connection
O {: l; W4 l) l" Q% |* G0 Kpthread_handler_t handle_one_connection(void *arg)) r& B; w- X5 o/ g5 s, w% h5 f
{
+ c q9 X' a( |" D+ @ do_handle_one_connection(thd);
9 W/ v- _% q2 C( v; k8 W1 N2 Y}) ^5 k4 R$ h) T& q& M( ~0 ?
//继续处理
* y& Q6 ^3 q# T/ s7 q0 x3 |! Uvoid do_handle_one_connection(THD *thd_arg){
1 |0 H3 H* u- ?6 t while (thd_is_connection_alive(thd))
2 @' j9 a8 n6 T" q {
5 }+ x4 Q: V+ r/ k mysql_audit_release(thd);! `& w! K" @+ @7 l$ S( i0 G' d
if (do_command(thd)) break; //这里的 do_command 继续处理
) T3 k9 D8 c( L }
* g* t- u1 @, I. w' o% @: P}
5 O g' O! O& n9 W//继续分发3 Q0 o& |: k: ]" ]3 {; ~0 d* W
bool do_command(THD *thd)
, d, \; y7 g B0 e* t{
: R/ Z) P* X9 ?7 D( V. n4 S return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
5 C& b" e4 ~8 \# G/ z |}
0 j* F ^ ]- R5 H' Y6 E, `bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
" C# q- ]- I3 G* A6 m( g$ s{7 A! C/ F T) ]6 ^1 p+ V& ~' y
switch (command) {
' {$ `8 C$ L1 a# x case COM_INIT_DB: .... break;
0 ?. K; N; `+ Q6 o% a- L8 J ...
- b6 X: m( E8 N, b% D7 ^ case COM_QUERY: //查询语句: insert xxxx
+ A4 \/ N$ b0 J4 P! N, Z! Z, ~* S( C \ mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析- z4 e8 z g# |" G# h6 ^
break;
9 d4 b4 v2 ?$ Z0 R6 a" k8 w3 B }
4 s0 U' H& a( e$ [5 o! S+ I}7 V8 L* R4 j4 y, R) W+ K1 j' @
//sql解析模块
2 N( ^7 w! I& G, ~) @void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)$ N4 V4 y- k* z1 }9 r8 w
{2 X/ d0 u' a8 B$ }3 O& v3 o
error= mysql_execute_command(thd);* u" S! f6 M; W% g" I' m% E
}' Y" ]9 T& \( ?# ^. S1 `0 c7 w
* f f$ \% N' _ B/ Q
* S( F! r8 P6 K1 \) b" _( Z
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
& X4 W3 E! q- b4 O$ N
; J. y& G' P& b" ^//继续执行
: h2 ] J& Y' C3 R% t: Tint mysql_execute_command(THD *thd)' E/ F Z- w# m& K' ^' i* a
{
" y' c' m$ T- b: e4 E0 l3 `8 F$ a switch (lex->sql_command)
2 B0 m0 I, y+ n9 l$ {6 ^1 `3 l* x8 J {
# J! }5 v4 s F: F. g* K* ]& n/ C case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;6 A8 I" |$ u& ]- ]! Y4 l2 n
. f1 h, q. K5 n l) L! b/ \ //这个 insert 就是我要追的9 Y& L" i2 B/ z$ M% ] W7 x
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
1 \1 W. w7 L# K3 X n+ s7 ~9 C2 o lex->update_list, lex->value_list,
0 H3 h8 p- b) b( c9 F4 m lex->duplicates, lex->ignore);
! Y. L& ~! j s' }/ f4 q& _ }5 B- R- t1 ]5 T! q
}
$ p0 Q* c8 ?9 O2 B! l1 y* ^//insert插入操作处理
* O- i" @! o/ B! e& e. U3 l& h4 Tbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
]# f) i& F7 b8 o @ List<Item> &update_fields, List<Item> &update_values,
( ?, L- B( k% `' z) K7 F+ S; k enum_duplicates duplic, bool ignore)
/ X! j F4 M- M( u. q" y. M( V% g3 X{7 g G& v! P2 a
while ((values= its++))+ K- s' p. n, L+ h3 B; |' S
{
V: z6 j* U' h6 s9 N error= write_record(thd, table, &info, &update);
; z1 c H( {- c7 K4 u }& _% y3 ]3 g+ J, d: N9 R8 C( z( m( s5 d
}
- k0 j4 J2 R6 Z2 j/ v* B7 Q1 g//写入记录
* t2 d# h3 S9 u3 mint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
7 z/ W. R; n; e$ H# [ s; y& N2 t. e{
3 p% D# K+ J1 M `5 V if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE): ?5 ~* E4 J& U' s' `
{1 h& f6 G$ [2 S/ {, y
// ha_write_row 重点是这个函数/ c2 _0 L8 z4 S' l
while ((error=table->file->ha_write_row(table->record[0])))
8 _9 z4 T. J4 E( |% ] {
. K& Z- v3 Y1 T ....
; K+ E5 o5 f5 G }. Y3 L+ |1 `- @( E% B
}2 U! \, G [% n$ d
}+ Y6 @4 |' k7 ~! L! K$ u" n; ^
+ {2 s5 ] l0 ^ R, p- |
8 P+ W! O7 z+ @3 J, L; J6 Y3 }
2 J; E4 \% W4 [) l. r! e0 v( O3 j可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
6 s7 G, X6 v8 u. ^) b& D, n% |. G+ B/ \3 a7 R& K3 C/ ]% t
<3> 继续挖 ha_write_row2 K* n* W+ A4 V/ u
( Z" D6 {( [) m
int handler::ha_write_row(uchar *buf)
9 z* ^3 o1 k8 v2 u4 ?{
) a: m7 a; g$ b) e2 {& m9 n4 ? MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })9 A7 m: G1 t. ]" a# N' @& p0 `
}% t" q9 X% b ?! A
' r7 _0 h5 V6 n, s
//这是一个虚方法
$ ]1 r9 b" r: Y' X Evirtual int write_row(uchar *buf __attribute__((unused)))& m3 G( y7 d% N( a6 H, n1 A g
{7 R1 ~- {: G0 {5 ^: H9 U" T
return HA_ERR_WRONG_COMMAND; p1 h3 a8 e$ w+ u2 P7 h% |5 ^0 u
}
: Q: h( O" j+ P- Y* |
9 @/ {$ Q) [# y: V; I: T e8 L1 [2 x7 ]
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
3 h N/ n1 Q4 g1 [3 W# C) W3 r# H4 u. P) X; V! H) M# v& H
3. 调用链图
6 ]4 _4 A7 h: x, E+ d+ T# k9 a: A这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。8 @% ~& A5 i3 |3 o# ^2 K
1 K& `# R. i6 S L" j
/ P* W, l" `6 i1 ]
4 B. T( f0 r" D) X' d& l三:总结& I- n" I# d8 {
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。& F: O: J+ Y* ]* \, H
————————————————: A. W$ I6 c8 f! H; o/ E/ M
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
% {; b2 o( a4 S$ J7 v原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
, o& c8 T7 s& V% C |
zan
|