- 在线时间
- 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 田老师国赛冲刺课 |
一:背景
/ [7 ^# _. R- q( R+ R9 T- z1. 讲故事' y: f1 H. s/ n$ q; V7 W- E
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
* I3 ], I, h/ I5 Q* @
4 Y# N; [, t; [) t, U7 B二:了解架构图
5 b: g4 j$ F3 S3 u8 amysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
, [8 f7 l0 _/ f1 y k# I/ n. x/ r i3 b7 P C! R/ O8 V! e
1. 从架构图入手
1 W" y* i! Y0 ?- b6 m大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。9 p$ k( m% F6 ^6 b0 k# `& K
) U( f; ]! V: w( J; P2 g% j# E) E6 E2 m' |* r0 X& x
* s8 Q% k: n- @其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
" c! P1 \: K: }& A! ]& D( t! i" v! X1 t( [( L$ x# \
2. 功能点介绍3 E# E, {7 Q4 j5 k
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。" z* r' c# \/ i" \% R& }
% |# x' L! W6 R- r |2 `
<1> Client
% J/ O6 K4 `; j& `/ T+ L: E) [' z不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
+ a1 t) h. Z6 @% [& |; S' ^& l) g* H/ A& _$ ^! B
<2> Connection/Thread Pool6 n2 U N. F! S% u; O: u6 j
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。+ T: [ z( k2 H( p
8 Q9 E, E) W; S* e$ i4 a! h
<3> SqlInterface,Parse,Optimizer,Cache3 f6 x/ o& j! i& N' L
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
( [, E; E8 o! } y, |8 s
: [' _* v: }# ~/ E<4> Storage Engines
% q6 q& v( ?- g# Z+ b6 ~* c; C负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。: i( S$ W/ Q. [# f
/ ^1 y v/ z% x0 f
三: 源码分析! T+ R9 g) Z5 h5 m
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
+ Q3 I3 |! f/ W2 Q l! `1 L- {0 M; v5 C% H2 d! b2 w1 U4 z+ o5 r% B
1. 了解mysql是如何启动监听的
3 @3 ~/ `% P$ a! j手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。; B" u7 S3 c( z4 j J5 @2 Q
: ~- Z4 P9 b) i4 r: w
, J/ K9 x! A. M O
3 N% J/ E" [& l从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。% l& k0 K) ~: Z7 Q) S) d9 t
@, v+ {/ t3 T
<1> mysqld_main 入口函数 => sql/main.cc
' h3 \! p A$ M) r; N N* X1 K3 o$ `2 W; H& s2 Y
& s" @0 {5 E2 a7 i
extern int mysqld_main(int argc, char **argv);
$ e+ {* q$ F5 a) c! A9 P* T
' x& N4 _: d8 ~* H- Nint main(int argc, char **argv)
9 I! ?7 _4 b: q& `+ V$ Y{
7 j7 X$ y8 L+ U# Q) u return mysqld_main(argc, argv);
2 q0 i1 U4 v5 ^; O2 P}5 y2 a: y3 {+ f' t$ c
4 V& s2 O2 S( @+ k4 x+ ?+ j
. B, o ~/ K! v+ h. N8 W, b这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
6 x' w8 H. }& c; R- P, j
7 U8 a1 N" ?6 b0 V& p1 ?% I: `9 c! [<2> 创建监听
6 O# Q; R& j5 ]5 L9 b- ?% [5 `/ u# L1 V- r4 d' T7 ^9 K- h
- F& C$ H7 a) ?/ |# Yint mysqld_main(int argc, char **argv)+ y1 ]' w- k2 k
{4 t- m; |* K) U/ C" C7 @7 m
//创建服务监听线程3 z2 _( v% p* P$ z7 p: x$ `
handle_connections_sockets();
$ ?9 L( i! g% h}
4 S* |: t- y: R; e. Q1 t1 S6 z. d4 {% r ^' P: a" F
void handle_connections_sockets() y% i8 F$ Z5 w
{
; h% V$ M* Q. Q! ?/ g //监听连接
- F' w, D) R" S3 ^: a+ K7 H new_sock= mysql_socket_accept(key_socket_client_connection, sock,) N) H6 [) H8 G" l
(struct sockaddr *)(&cAddr), &length);
6 s D5 A3 o1 B9 h, j# H, F5 w2 O" o: A$ v U& w
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
% W C( l- v! k# q$ G0 Z thd->security_ctx->set_host((char*) my_localhost);2 J4 M' J# V+ ?, Q' G. K; a1 C
6 r4 u o1 N7 C3 _/ U! R //创建连接2 j* s) c) Z. }4 @1 ^2 B& o
create_new_thread(thd);% Y7 X6 `( K& d" ~1 I. u) T/ n
}5 b' x$ z) F8 ]6 y# t6 x
5 Y8 S! D& v ~& v# n3 ^//创建新线程处理处理用户连接
3 X, f! m! c7 D6 ~static void create_new_thread(THD *thd){& ~/ H" Q! _: V$ _3 ~
* @" N& q6 A+ V1 n J3 t0 ]: L
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;) @ R: Z- h9 ~# w# n
4 \4 q O! N2 R- A a* C( W
//线程进了线程调度器
: b3 S; a3 R6 }# X& ~ MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
0 ^' x! n# p' s. F$ Q7 ~}' u& C8 Y( T) H- {
$ x% S7 _3 m& K8 {" ? `$ D
: u9 v2 G4 D X! T5 ~至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。) [! D# U1 r( E) c: Q
0 l: V* e' Q) B b8 f
' B$ ^- Q+ ?! d9 w# x2. 理解mysql是如何处理sql请求
$ B0 U0 Y9 y1 ~1 v+ r* e这里我以Insert操作为例稍微解剖下处理流程:
" s% T4 `$ e+ } K/ a+ Y' b& V
7 o! \# r4 {5 c* w当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。3 l K/ F$ n7 E$ H% Q! G4 T
- Z5 b& k" b8 W; x5 Q( Q# Z
5 n# m9 F& J! O1 [ \, ystatic scheduler_functions one_thread_per_connection_scheduler_functions=9 M; R5 S' v* j/ [+ d! O
{
2 a. |) @' E1 t7 f' _5 u' r! r 0, // max_threads
6 Z4 [+ p# O! s4 R6 S8 l6 p, L2 @ NULL, // init; f4 r8 A+ b3 D: r; Y. n5 D7 `
init_new_connection_handler_thread, // init_new_connection_thread2 b5 Z% c% z- \5 ^: b; |
create_thread_to_handle_connection, // add_connection) S% l& _3 d0 B" C5 l6 c: |
NULL, // thd_wait_begin- |( d; v$ x/ s) D5 c" k! N- Y
NULL, // thd_wait_end1 Z: Y7 ?3 c% _2 M- R3 q
NULL, // post_kill_notification
4 R0 x% ? x, s2 G2 G3 X one_thread_per_connection_end, // end_thread
a: d' Q. @0 u% d4 q4 q+ P! v NULL, // end
3 Y( C( U6 e2 U7 i( D6 B! ]$ s};
8 z/ \& Q! ~; m4 F; I& e, r# J& W! n! B/ k/ ?) q+ {) w
1 s! s" [5 i8 F1 [
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
) o5 D* m, k& Q0 s5 Y$ V0 O, X: c, c7 N# k% u0 ^
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
5 S2 R. M9 y5 O! d/ r
0 Y: B' x% k2 Y( N# Fvoid create_thread_to_handle_connection(THD *thd)# I! r2 W5 p9 a' z6 S
{# U, j# O% n5 H
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
; H1 W9 G' \* U* b handle_one_connection,(void*) thd))){}
& u& [6 k1 i+ X}4 Y* N& H+ U7 v$ z5 h( w6 k; r( Y
//触发回调函数 handle_one_connection# q& q6 S2 a! S: P3 I" R2 |
pthread_handler_t handle_one_connection(void *arg)
9 M! U$ V# A6 L6 n) i( k6 N1 W{! `' W& I z2 y4 {
do_handle_one_connection(thd);
$ Y6 B8 D% e) ^1 a}& U5 c; _. i( j# x& c; _
//继续处理: A, Z& p1 |; k) ^8 `) M8 t$ Q& N% z
void do_handle_one_connection(THD *thd_arg){8 S7 Q3 H- q* L2 B8 c9 O5 S
while (thd_is_connection_alive(thd))
% _6 O1 t* t7 ~9 { {! M) S% L$ i, H$ `( y! L
mysql_audit_release(thd);. g4 {' D9 q5 M% {& _
if (do_command(thd)) break; //这里的 do_command 继续处理* m3 D& O( r: C* a
}$ p7 x" \6 V% T3 J) f) i
}
$ Z. J8 H1 z# k! i. `$ r" i4 P$ H& ?$ T//继续分发) Y2 h# ?* }7 `3 ]2 g% G% n2 y4 [
bool do_command(THD *thd)) o3 i# O" a( P' O9 b
{7 C6 R) Q) Z+ Q9 [6 M
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));5 J/ X" B! L7 m0 {7 P) E
}
! M3 l# q% G% |; `5 Wbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
+ j/ C8 @# y+ c# ?{
$ J' |( _; g$ I switch (command) {; ^ y8 q: ~1 {7 O- ]$ ~. M7 c
case COM_INIT_DB: .... break;
* \4 ~( ? X$ k ...2 F$ x; G& h8 ]! M' ]8 C8 g
case COM_QUERY: //查询语句: insert xxxx: j3 k# M/ G+ \/ q
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
J: u* \0 g0 I$ | break;
; U; M9 m+ _7 |* q' H# U }
9 A" S8 ]3 d( x' I}
* O& e3 Y0 H- L6 {! v/ Q# [//sql解析模块+ @" q6 l L/ y
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
0 L8 G7 W/ S! n" T{/ h7 ?# K9 n7 [) i3 P3 L
error= mysql_execute_command(thd);$ p: Q6 ~% @' K" T% P, k% [( l
}* Q4 I- \9 t) P" s+ ? {. g+ y
H: [9 Y& g0 o( ^1 D+ A
; F6 x/ N: @3 y1 m5 T0 @& R9 ]<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
2 y5 ^( E& p3 h5 H$ ]/ B& F" {' l# W3 L
//继续执行
B. I, A3 U0 ~. eint mysql_execute_command(THD *thd)
7 t/ O x, E z5 }{# G5 c+ P8 x/ c0 F6 G- _) t
switch (lex->sql_command) 1 p( c" W' U4 k+ \, ~; v
{
4 _* \) E: K+ M6 b5 z! |" [ case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;3 n l/ Y1 f, h0 n* G
+ \* d w4 |# E3 y/ h //这个 insert 就是我要追的
4 s$ I! P7 C' f5 l8 F% `8 h case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
; a6 A+ ?$ A9 ^2 l: g7 ^! |5 m lex->update_list, lex->value_list,# `' d1 W% K7 f7 j* _* U( d
lex->duplicates, lex->ignore);
3 p- W* P% x) m) G$ I! L }
3 ^, @; f4 D+ T1 k}
# a2 z; {2 Q) t5 v4 @6 m//insert插入操作处理/ B: l* d3 |, I# g# ?3 a; L; f
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,2 e" t* L& `* f Q$ X. J
List<Item> &update_fields, List<Item> &update_values, 2 g" V$ g. S' c! Q0 ] u
enum_duplicates duplic, bool ignore)$ C* r+ M$ C" ], m0 `5 R% b& p2 A
{7 v! K6 Q2 p+ G5 \0 C, O; g% ~
while ((values= its++)); Z0 _0 I3 R; k! w1 m& S3 y
{7 P' L+ j- m4 b/ ~3 H
error= write_record(thd, table, &info, &update);
* {; C0 r: N% @5 L* i4 b }
1 y9 `8 O ]8 n3 [% N( Y; F}0 E* M1 _; X6 k _1 N" [
//写入记录& [$ f0 F9 M w, s( E# j/ C
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)# m* E( @& w0 `9 c W% o
{3 J) t" J9 S3 z! H) z7 k) O0 ?
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
, ], p+ T7 J- v) W {
) Z( S: F/ a! j+ { // ha_write_row 重点是这个函数: |! q% U# a. {' m! R: n( P& z
while ((error=table->file->ha_write_row(table->record[0])))3 {0 I3 n1 |6 M& G/ S
{7 I9 d+ w" G. m& a* _
..../ B: l7 h& x$ ^1 N, t( b
}/ H' l$ t# b( j2 ~3 h
}
" i+ N3 O! B' k: y}
6 Z/ N" h7 N9 N6 ?( {! J' \
& Y2 q+ p% ]& {
: f% s0 H& x3 W4 n/ c' m o7 `, \9 { g7 `2 n P
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
1 w- b T; z4 a/ H9 U# t! Y% [. D
5 f1 P: i& ^+ F3 J5 ^<3> 继续挖 ha_write_row/ T$ d6 d/ g6 N# L& d! \
: x: F4 r5 D, X/ P. H! z
int handler::ha_write_row(uchar *buf)
8 z5 I, D7 ]0 p* i% Q. p{
% X. w6 |- k5 z( }" I7 M4 ~. _7 n MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
% R7 {/ ^7 C6 V+ n5 z. E}
5 A# f1 t! P4 D) g8 o9 z# V9 e
7 P% i4 m/ t& i7 h" ?+ u, c& k//这是一个虚方法
& u+ V: \3 F, P+ S2 a3 C# S1 Rvirtual int write_row(uchar *buf __attribute__((unused)))
' p' S9 W7 e+ Z{ h: |5 j- L7 y+ K- t8 @# D& _& O
return HA_ERR_WRONG_COMMAND;
' ?0 D! r% E% P' z}
9 i, o+ v: ~+ t! N3 l& T% K2 o
1 v# R$ p% i) k" ^: x; d7 m( g' t" N
) a) q$ |+ W( v6 U$ K看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
7 Z% h) g* Z6 c$ r/ \# s( K5 s- M/ Z$ W
3. 调用链图
- N. v9 X2 ^9 Q( p+ x这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
5 ^. V$ V' i0 x( y) e
& C( W+ L5 O* U+ z- E* a' J6 K3 g. s0 Y( e* ^8 `0 t' z
0 b6 s+ y: |9 F: {- ?% N1 y2 n6 d
三:总结
# C M6 R5 ?5 }, Q* Y大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
! {9 y$ G3 w1 @" E& D————————————————
; q1 }; R/ F% B: z版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
9 o6 X0 ~ b3 T. s2 ^3 Z原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415- I' { S9 w; d% L" T D0 Z" s
|
zan
|