- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55505 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17603
- 相册
- 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 田老师国赛冲刺课 |
一:背景% T# P: D A% D3 c7 `4 m
1. 讲故事, F2 K7 K: m: p$ }: X
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
6 [7 B" O z! O. Y8 ]+ C# Q* T. X# D2 F! q% n4 K
二:了解架构图$ I4 T$ ^4 C6 R& j1 H) m
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
9 X9 G0 L/ Q: Z8 M3 D/ T0 a8 s k: ^' h* W
# A2 {, k% f, ~9 F" j+ y* {1. 从架构图入手6 s7 w7 [: D4 \& A& f! O
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。4 w2 }3 R$ D) t4 o4 [
![]()
" b4 R' [0 O( x' D/ l9 }4 F$ u8 H$ Y: O3 m% H) m6 t% n [
7 a d1 n5 H4 B! c
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~' ~+ r6 A7 ?9 l5 m+ H- Y; I
5 N# b l2 ~; E# q3 }' \
2. 功能点介绍
* R, K& y5 b# OMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。' z& Z/ k6 j* P
# V; `# B$ o3 F/ p<1> Client
+ A6 w1 ]* o- W. r( l不同语言的sdk遵守mysql协议就可以与mysqld进行互通。2 v7 _, S* B- L% f# ^. Q% Y+ j2 U
! R' p# f# R3 ]7 Z
<2> Connection/Thread Pool# t3 I8 m* J4 A5 H4 a
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
0 M6 B2 i& A6 d9 t( w i/ _" X |' {& Z; p
<3> SqlInterface,Parse,Optimizer,Cache
3 @! x4 e; ?* M1 ]& @对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
9 S3 d" b+ D% n4 j/ |# j8 O
+ n; ]- G4 P( _5 h$ C/ O<4> Storage Engines# N$ |, {1 a$ }: W5 v {' M) f- L
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
% U& O5 H9 d* [5 L. B4 O+ K+ M T- u2 A; r
三: 源码分析 n9 X3 ~6 o. _
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
0 F1 Z$ N% M! n( j
) m" }5 R3 v" `: i1. 了解mysql是如何启动监听的' d& e/ O: U+ h8 ~3 Y9 |
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
$ [$ `1 T U7 F) U$ h: k$ W ( e6 N! I" z+ V5 k7 ^. y* ?; s
6 I# b1 I I2 m) w5 L
% @1 t) c H. k: D4 q @从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。/ h( z2 R2 |1 D+ ~' b
4 `: l8 e9 v# P& N% G! A. u<1> mysqld_main 入口函数 => sql/main.cc4 I8 `* ]8 o5 t6 ?9 I4 [
8 h7 i9 ~8 L) u( {% J) g
' S& \2 ^& [& U0 t# k/ O
extern int mysqld_main(int argc, char **argv);
1 k5 L8 o/ v( _+ P( C5 t! W9 A$ r1 v7 Y& w9 ?) m' n; I8 X
int main(int argc, char **argv)
# f8 S; G+ O, ]{" j/ y {. W7 Y4 `1 M6 g& g
return mysqld_main(argc, argv);
) ^' |( A/ N1 {" r/ _}
# o. v. B) I# N7 R7 ?0 t1 f/ D
& g2 }, [+ g9 G6 B5 `: y
7 v! t+ R; @- O/ g这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
& s3 b9 S* f* i7 r x1 t* W/ h$ |; U# A+ i/ d
<2> 创建监听
$ @6 C' ~, r( T4 Q, y
9 t5 G& f7 e" J
B9 b; `6 e1 o/ e* d( |) q! hint mysqld_main(int argc, char **argv)
6 k/ ]9 b# Q: P{
* x1 v+ A2 r0 V% W( R# |7 | //创建服务监听线程* _3 n% k- p. n p ~5 O0 k
handle_connections_sockets();3 w8 O2 |" [+ m, S2 h
}
1 v: l6 T8 }4 H% c: K" n) z5 f& M* n. j8 Q6 r
void handle_connections_sockets()
1 o( K( U8 I& a C7 Z{
. q" y0 Z8 Z/ k+ Z; j0 I1 ?/ N8 E //监听连接
1 C0 q: e3 f/ \" ] new_sock= mysql_socket_accept(key_socket_client_connection, sock,
+ `' X, x. [) P3 k& S9 L1 F (struct sockaddr *)(&cAddr), &length);
( w& k& z, G3 r! h8 ]0 e6 |7 D s1 K% K* g# f8 n; M
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
6 }- M8 n8 j( o2 p" l( a5 O1 c thd->security_ctx->set_host((char*) my_localhost);6 E+ Y$ X3 \3 y
& U+ a1 {& e# Q6 \9 u# D //创建连接! e" }( J5 {4 G0 e
create_new_thread(thd);, E& B' n8 g4 R7 k
}5 J" ?9 ]! \" |6 u
2 _! q x2 n8 p: F& z0 R. n
//创建新线程处理处理用户连接9 ^; j, Y/ G3 j. l {3 G
static void create_new_thread(THD *thd){
' D$ ?; P8 }, S+ l0 X
. i9 e0 l+ k0 _; ]' X: p) R thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;" F& |7 g6 n k
3 q% |' U, D$ ^7 w //线程进了线程调度器 \. u. e6 L; d
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); 4 l2 M6 Y2 ~1 g$ S% a1 J) W
}
; ^4 |7 L0 E' m/ B* t: Z' v9 l4 F( `! c0 `0 U
7 @* x5 i5 I! [; J' C+ ^
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。! e) T! R; s9 o: C W8 x$ G
( [: d: E: M" J$ x
1 i; M" v/ |- m [5 l+ I
2. 理解mysql是如何处理sql请求
# F( n: k$ I7 G4 I4 P5 c! a& A这里我以Insert操作为例稍微解剖下处理流程:
7 F$ |9 _3 ?3 J6 P2 _7 y F1 B( L3 b* l' l
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。5 j1 x3 ]; x7 k9 l, Z
8 ]$ I) T4 \, n( N4 w- }+ {/ [+ F. s2 x! b1 d
static scheduler_functions one_thread_per_connection_scheduler_functions=2 x7 d- t. E' u. [! |( g
{8 n# f" Y1 ^2 U$ B1 ~. \- H
0, // max_threads
7 T8 y- U' W/ h3 a NULL, // init
+ u$ V3 i3 ?/ N' \ init_new_connection_handler_thread, // init_new_connection_thread
# U8 ~" B7 ~9 P" R* l" ]7 F create_thread_to_handle_connection, // add_connection
5 t* z2 }& W& k: V NULL, // thd_wait_begin/ V: s7 Q/ h* f) D" ?2 k
NULL, // thd_wait_end& ?8 C# Q; l! [$ W
NULL, // post_kill_notification- e; I) [# v- o u# D/ r: T
one_thread_per_connection_end, // end_thread
. ~4 b) Z8 H" T9 K2 F0 O) G NULL, // end
. r. X: x$ i$ {$ O- L};
/ i- \1 s# Y- A' e0 g+ P
/ \ _# m# i3 X8 @
8 a/ l( {* z3 b0 }7 x6 z, {) I从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。9 U. o$ F: D& C/ n, k" m2 y
$ p1 w# c& N, c0 D q5 i4 [<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪2 O4 e1 p8 A3 w9 ^
: {" y" Y. f9 o: J4 Avoid create_thread_to_handle_connection(THD *thd)8 C0 r8 g8 n, ?* a4 ^! Y
{ q, B& J. l: p
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,3 u8 U) T: m0 w B! e" l& r
handle_one_connection,(void*) thd))){}2 r" @7 L: ?0 W Q
}+ n8 d: F4 y L( Z& P) ]. e+ f. R
//触发回调函数 handle_one_connection
, [, o) H/ Z% n' M7 zpthread_handler_t handle_one_connection(void *arg)
+ a2 [# H. K$ T5 y/ X4 ~) ~7 Y{
8 d# v' e* a8 @: \$ o do_handle_one_connection(thd);
( E! P; j) `# q( j* z! g}
; n0 j& e2 U# s/ Z7 H8 {//继续处理
6 v1 K/ i0 m' r9 s6 ~+ @, R% tvoid do_handle_one_connection(THD *thd_arg){1 N' V, S" x8 ^9 k1 j0 T
while (thd_is_connection_alive(thd))' ? d- P* S0 Y2 t) n. _3 a
{
1 B6 R. k o' \2 i& T mysql_audit_release(thd);5 ]: P/ L) @; y
if (do_command(thd)) break; //这里的 do_command 继续处理2 }( ]" K* T/ v. R+ z" N
}" G9 _( U4 f% C8 v6 R7 @1 v3 Q
}
7 Y$ K5 c: x' M$ O/ A9 n, I//继续分发
! N/ H$ W" ~$ u4 k! N+ Q0 abool do_command(THD *thd), A8 G2 i- C0 }" ]2 t2 f
{
4 ?7 I% n& k; ?2 ^: s return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
2 d/ S8 W2 L$ Z& J# ^% ?+ w}
, s! X" |( o8 \bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
: F0 `$ c# x8 \8 u{$ O2 z: z0 `8 y+ i/ i
switch (command) {
, @$ v) r: i. W2 C case COM_INIT_DB: .... break;2 d0 w6 C$ @3 E f" ]7 f
...8 E" M9 L) l! w3 ^
case COM_QUERY: //查询语句: insert xxxx [& ] {% E2 O3 X. D
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
% D2 C7 x0 _7 [) n9 U/ g break;+ e- R$ f3 R* d
}* m6 B0 Z5 d' H
}6 J7 [* b( J* J4 @7 X m
//sql解析模块
+ g9 q! e' J+ H/ bvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
M- D& A) R/ b. J& C5 G6 O{6 O" Z) A2 `/ G$ p0 @
error= mysql_execute_command(thd);$ Z d- o# `1 w; X
}5 V5 ]" Q r- z v0 u5 }$ I
' v/ C( U; Z6 {' ?) v- N) e% D! H: [
' `5 u8 W+ P, Q; F, K( @<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。& @3 Z$ E, L. U9 R! n3 K
k/ `. k0 Q7 t( g//继续执行
9 Q, z+ C3 h. Q, j6 C9 @+ p2 y" jint mysql_execute_command(THD *thd): p K# H7 F# z% r- I
{
1 \6 F1 V0 [8 z8 E- Z& } switch (lex->sql_command)
8 O) A3 ? ~- Y, n { ^5 U4 o( G0 H
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break; ^) C0 z# l1 W2 k5 `1 [. Z" z2 g$ Z' C
3 \) S1 j/ j/ u) S! j; H. P //这个 insert 就是我要追的" m9 f. Z J( u4 b0 D1 w
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
/ K N; I2 A/ G% \& g* | lex->update_list, lex->value_list,
5 Y$ m E7 A3 t% b Y7 Q lex->duplicates, lex->ignore);$ P1 X# k% P$ B$ B- x& `$ `5 }4 K! l
}( M6 f6 g. s0 P H( [
}
% v/ ?( j+ \# ]5 r6 }//insert插入操作处理
9 i' d- g/ c& y: `( w7 ^bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
' i0 i/ g) p! {* j' C List<Item> &update_fields, List<Item> &update_values,
: |* L/ e/ h0 l$ D5 P enum_duplicates duplic, bool ignore)
4 A8 k2 N! E0 i' p, E* _{
; m/ R6 `' N3 t while ((values= its++))- t2 i3 P7 C& A; l
{
7 z. P/ I9 B! \: C7 Z error= write_record(thd, table, &info, &update);
4 }9 R0 T% D6 R5 `: v* K! ]/ B. B9 P }1 L6 y7 }3 ^+ H+ }) |4 g
}
& g+ ?( Y7 Z( A3 U* w//写入记录
( i4 H$ w& S* G$ x/ b' B Rint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
* r& f! C% D# k: \3 z m{
4 `2 p3 f- Y" J: N U+ Z if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)) e$ T5 Z9 Q% e& c- y
{
Y9 i/ J- ?5 d! o // ha_write_row 重点是这个函数, h6 j+ p& I' f7 M" A5 B
while ((error=table->file->ha_write_row(table->record[0])))& m0 p) X& a! d. O
{: k2 ^8 s q6 F* g" r# l8 }$ [, E* C- o
....
) w* Y- w) G5 B# p7 g! `% L( a/ ^ }
_, S, ]! v6 m }
/ b8 a P) V$ x& r% J1 E* s# c}
7 f' Q3 L" s( i5 S H: Y4 `; C6 h }' V/ G7 h3 u. N
8 \( l7 g* Y8 b- R& @$ j
: c4 q6 E$ W5 }/ [2 }; x& p可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。) N3 B* Z) d- P5 s6 S2 _" W
z' e9 t) ]3 B6 Q. H: W0 X
<3> 继续挖 ha_write_row% g# u9 a' L& d* k# Q& d
4 r/ t6 u" q4 X1 ~4 kint handler::ha_write_row(uchar *buf)
$ X1 G% u+ O8 T5 i2 X' x{) k) s6 I* L% k+ Q2 r+ y- z, v
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
8 F( x( n) j: ^+ O, U1 g}
: q8 [) j* F8 K: o7 H' d9 ?4 J
1 f9 D1 E1 H Y, |, S//这是一个虚方法
& r' h7 t: |1 [0 ~. L% Lvirtual int write_row(uchar *buf __attribute__((unused)))
0 u' ]6 `4 L/ ^- J7 z1 `$ n' G0 C{' i6 |" M8 g3 c1 m
return HA_ERR_WRONG_COMMAND;' {4 Y4 x' B+ u1 S
}
t+ y9 P5 m. `1 t* {( ^7 e4 a% T& A2 P
- x5 Y$ u! [5 O: w& }* T! D0 ]看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁$ d! B& ~, V$ o! F$ I9 f5 L
; O) o8 f2 \! l a' T/ u/ _3. 调用链图
# I! x6 Z G& q# X2 f' ?7 q; ^3 {$ C这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
4 u% R6 H* a2 K4 h a # d% a$ l3 G; D* s
! C( U- d$ D3 G ~# q6 X
! [0 Q3 Z1 {: r' N S三:总结/ j$ L3 `. U- b* W6 G
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
; {. ^7 L K% C* f————————————————2 A" Y. D% u L$ p
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
0 U; C" s6 d' n3 c! p原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
: y: {% v; {# q6 h3 ~$ x4 n- b |
zan
|