- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55572 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17623
- 相册
- 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 Z* Q% e% G; t! _1. 讲故事0 C0 j4 U$ C7 N J9 f* ^1 m8 r
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
, N1 D- |+ B6 q" C% y8 q) q! f- K9 t$ Y6 R' A
二:了解架构图
8 t! P+ }8 T3 x3 mmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
* {5 ^! `) Y n* }: O+ d
4 A) `7 }% B% C W- V7 T" `1. 从架构图入手
2 n# h; m+ \2 D大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
) e$ @+ A y V2 h) Z' o& ? 7 l+ Y1 _8 R! X! M$ A A3 i
9 Q6 }7 p: F, \+ `6 z
W! K7 V; J& g5 F A
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~$ s' u1 h5 T/ E
7 s- |- V0 {0 P; U* H8 d7 m6 C2. 功能点介绍0 r5 `0 X$ F8 R! U4 Y4 S; v
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。/ Y( i3 ^- M0 M4 @2 s6 |
& a, r: @1 N! }* `
<1> Client
9 M* F: E4 O( f; C+ U0 \4 O不同语言的sdk遵守mysql协议就可以与mysqld进行互通。: N4 \* @7 J0 d. _
& W# Z# l a( ?; j
<2> Connection/Thread Pool' E8 J( P( z5 k* ?; c* A
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
+ i! k) y! G- F0 k- }% f/ Q1 p" u; Q. g- I, Y# O
<3> SqlInterface,Parse,Optimizer,Cache
8 p T' i9 U0 {0 h, }) A对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。0 }; a" @+ M& }9 j
) T! |5 E. a& C# Y" A$ }# ?' J* M
<4> Storage Engines
" ^5 y! d! X" y: D0 d8 f, b负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
0 R+ x6 Z% U. B5 M5 y" g) @
4 |. e& s' P" ]三: 源码分析
# m/ a1 f- _# i- U4 i5 L4 i关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
& u* ^+ m( ^' v6 d1 U6 c' K* p( }! U% g- D
1. 了解mysql是如何启动监听的
9 y8 Y7 [+ N; E4 f% C* a; y手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。4 I: S( R! P6 |, p. _% J( s
& q3 w5 Z; o# G* `
& S$ b) W9 W+ Q& ~& H9 m3 }( P( C# ~5 G: X5 D
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
7 k# F+ `' J- y
$ e( q. I; s) k5 K7 ?<1> mysqld_main 入口函数 => sql/main.cc
6 b _; H. o" d- f9 E$ r& Z" U, V
* f3 u; `! S% l3 ]) j$ L! wextern int mysqld_main(int argc, char **argv);
! p( \# W+ J: h4 a
[8 v" n: b9 f8 X$ z& Qint main(int argc, char **argv)
$ H5 H4 z5 ]( Y{
' L2 P8 b7 ?/ M( G: P; x return mysqld_main(argc, argv);
7 v# G: \$ B' ~& j2 V}
. O J2 e' ~/ \ v0 N% y6 F) W3 \) _$ W2 z4 B! a
$ Y% ^" d4 Z/ c) S- ?' } V2 I8 s
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。% T/ d! ?. b f# y
3 b4 f7 O6 g2 g9 A; S<2> 创建监听
3 [, m2 b' y. Z; p" F* h- A" w& k5 @: Y0 \9 K: i
|- L( Q: J( e j
int mysqld_main(int argc, char **argv)4 \( F# m7 e# E' X
{
* U" _. D5 P$ x6 M$ P+ X //创建服务监听线程
4 ^4 E4 {7 W, o# h; H: ?# B handle_connections_sockets();
- Y/ J9 x& `3 N2 ?}
1 P3 r, ?) e! E/ e1 g- z
3 ?" |9 ]* |4 U7 l6 s5 M8 vvoid handle_connections_sockets()( `. D! r+ Y3 ^9 U7 q+ J2 f
{
% c( V; d% H% F" F //监听连接
9 k, m, W' D& z% l* ], i1 I% ] new_sock= mysql_socket_accept(key_socket_client_connection, sock,
; p- r& g9 b2 ]! J$ k (struct sockaddr *)(&cAddr), &length);* ` [6 Y8 S5 r; M7 q5 T, j" d! i9 P
) O( ~6 Z+ S, G4 F6 K$ h+ h. I if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)); J& Z$ H7 O+ d, u5 O- X: c
thd->security_ctx->set_host((char*) my_localhost);3 r: j2 l5 z1 z0 z: X/ L o8 }+ n
C- R: r# D0 f! E
//创建连接
1 [9 b: r1 t. |# e7 p; M2 l create_new_thread(thd);
4 }# y& H1 a/ z0 q7 F2 x' s0 Y0 h}
8 f) l; N5 ?$ m) [# S( r& a+ v, j" Q, b7 b
//创建新线程处理处理用户连接5 }% A, [/ L) ]5 I8 v1 e; F
static void create_new_thread(THD *thd){
5 w) ~; D" d: F( T) q7 j+ R ?. p: e6 _0 f1 i
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
& G) c4 l3 O# u! `: Q: i
8 W) k# q* ~+ J4 ]) g //线程进了线程调度器0 A6 q) x' {+ @# W
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
0 c/ A: m4 u- |, W" q! J% h2 {}8 t, C. i# @; U6 a
2 j" k" r( R) A& }! W, t% T2 c# H
K: m# j9 q' K+ Z& \3 K5 _* ?/ |至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。; I* O" a$ o& w. `
. r* d* d8 |% J+ x. C4 y: w
9 ~* F) j- b/ f! [- @9 P; G2. 理解mysql是如何处理sql请求 }- C. k& X- L& c9 ~
这里我以Insert操作为例稍微解剖下处理流程:
: A* G: d4 o: C1 Q1 L% Q5 t! b( r6 |
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
7 p5 l1 G. u8 h5 p) X8 E% X. F7 G' \3 f
" F% d) G" \ _6 p: K+ J3 zstatic scheduler_functions one_thread_per_connection_scheduler_functions=5 y- y3 S" l# m
{
% ]& @7 o9 l3 J b* @) u 0, // max_threads
. t7 c9 m }6 P NULL, // init
' @. f- h: ]2 T2 M9 A: S init_new_connection_handler_thread, // init_new_connection_thread4 o6 Q( S3 Z8 t, _# }
create_thread_to_handle_connection, // add_connection
$ a7 e* I8 W8 q' k- g$ S NULL, // thd_wait_begin
* B$ Q1 I! ~# i NULL, // thd_wait_end
7 p$ j5 R" {$ n( y. r NULL, // post_kill_notification' i1 T% @8 s. G5 f: d1 J
one_thread_per_connection_end, // end_thread
, f; C) U# X+ ` NULL, // end, e7 w- v& g( j7 T+ Q/ V
};
! b X, e$ V2 Q |, M- c+ c7 K- A' U1 K4 G& m
$ E) s, n; F& M L) C- ]从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
1 B% t& y) R% ], Z& \1 Z( u4 o' \' ~- J6 |- \
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪0 J3 f7 R+ r: g6 R* ?. |
8 M1 _/ ?: ^9 x0 `' r% k7 Tvoid create_thread_to_handle_connection(THD *thd)
" y! i8 I, B$ w{
. k7 Q0 Y0 T" K* D if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,* l5 l+ j4 x" n, s
handle_one_connection,(void*) thd))){}
; S: e! f2 A# }9 \9 _- W" d* y$ `}
f- H# }/ ?. J4 A; m//触发回调函数 handle_one_connection( l/ a3 l, j( O% F
pthread_handler_t handle_one_connection(void *arg)
5 K) Q% Y0 P- v d9 ~- O, [{
" U; X6 D {% c6 ]7 E do_handle_one_connection(thd);
0 t( ~0 t8 ~6 U( @. i}
; e7 A* K3 D0 ~' s6 h6 W# g" Z//继续处理
/ L6 I% u6 ~! qvoid do_handle_one_connection(THD *thd_arg){
8 K- g! k4 f! e$ S: _( |9 Z b2 X. X while (thd_is_connection_alive(thd))
$ R( i+ J+ B9 y( O1 E$ y/ j% z {5 P7 `8 V. h* N8 \
mysql_audit_release(thd);
1 E2 J& k; j. R if (do_command(thd)) break; //这里的 do_command 继续处理
2 o. {" }' u9 h, l- R } H+ Z! e+ ]0 j* y2 t( T# [
}8 k' r: @9 N6 Q: `6 }% S
//继续分发* c% j" T, Q1 v! ^
bool do_command(THD *thd)
& v8 S4 P1 ? w/ o8 V$ G' b{
! M% h- A1 G1 h5 A. T8 B! E J return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));1 E& c; [; ^: z- T* m( a
}6 W; W' |$ k, Z! Y
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)9 C) |# b" ^) |
{
# U! L4 j: B. F' Y switch (command) {
" Y$ [; k. t$ d# O" B case COM_INIT_DB: .... break;# l% L: Q/ m4 r- i. o& m
...
5 A# y# k$ U: j/ ?0 E; @ case COM_QUERY: //查询语句: insert xxxx
1 \, U3 p6 B0 Q o8 o+ s mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析. P/ f# |4 O9 q+ Z7 C( R' g4 @; N! ]
break;. _2 A) H, H* } E1 D, O0 a: q
}! |0 |$ ?0 \5 K" F
}% l, F3 p5 T. t$ S
//sql解析模块( K" e# \3 v' @1 I
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
( ? f6 U0 A( U1 q" y- ~{5 O8 A" w$ R/ q* ]& h, t
error= mysql_execute_command(thd);
9 C+ U+ d/ U% q* h% k}* m+ [" d' z' l. l, _
" ]; g n' f+ h( G% x& N. L4 u5 J- e0 _! O# X! `$ f) ?
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。7 B2 @+ z0 e2 q. B/ u% S
8 k' W8 ?% h# _" s9 a//继续执行6 z8 L# k" \0 t- c: S( ~
int mysql_execute_command(THD *thd)
/ q9 I; D" q$ e{
& {2 _2 k3 _6 f6 H, e7 | switch (lex->sql_command)
0 @% A% z( j" _( S% n {
- `. h; P! M2 U7 k4 W case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;: {, b3 W7 x% X$ ?0 G3 ]9 h0 x" }1 G
3 x- c( }% m9 [) f2 _/ }7 V //这个 insert 就是我要追的
1 t1 @) g* c+ T3 g* ^( J/ I; L4 N case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,! S+ c" k- q# o8 ?- S
lex->update_list, lex->value_list, f( `' N* J* C) c
lex->duplicates, lex->ignore);) Y0 h+ V9 x9 ]+ t6 F7 Z, c2 R+ X5 D
}
4 O: c! _: i9 N" q# n}
; S: m) M) ]3 Q//insert插入操作处理9 N9 ?8 U# P" F7 y6 w
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
# G0 k0 k& q' ]7 M7 ^4 b T/ { List<Item> &update_fields, List<Item> &update_values,
) |- P: Y) {- F d9 W enum_duplicates duplic, bool ignore)( D/ U, R1 H; P2 v6 ~
{
6 U5 y( N# |) X5 X3 q9 h8 d9 r7 F while ((values= its++))
5 t: S" h3 s% g! N' ?/ _$ j u: l {
8 _, ]2 b3 ^8 k) c; E( a error= write_record(thd, table, &info, &update);) `! x, f1 Z. d6 A& w& K2 `1 ]
}
6 b" b# L, G, x4 V' @}
2 U) x( z( \* B% `$ p( L2 B& F//写入记录' b% ]! n/ x2 K0 `, ]
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)( g( ?( c6 i* \: R
{. H4 h# }5 k8 N$ b. `
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE). j; g7 |" {/ t- e% Y7 w5 p/ G$ }+ B
{: s( X( s% _0 M% s2 L7 g, y; I
// ha_write_row 重点是这个函数: x( {' Y" ~4 ~! d4 A
while ((error=table->file->ha_write_row(table->record[0])))
) x8 o) T: C- @! w5 l {
, D" K% f. D. ^6 {5 ? ....* J3 m3 E& S I& t: C* A
}
: c5 \+ l7 D; g; { }" c' [ }
/ ?: @: p/ j h+ d2 K, S6 D}2 z, l+ f1 s8 k/ _% |
9 O P/ s. v1 x* T' |7 ~/ k
: c3 v+ z! p3 Q. Z, b; v8 v, `: ~
/ z# H) w$ f$ k6 K: D可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。/ ]7 I: X$ k6 q! {, O6 j x6 H3 K
! U* A2 n, r7 w' `( m
<3> 继续挖 ha_write_row
, C1 G9 ?8 w0 E' O7 U! R. N" L3 m0 {+ [6 ^7 k Y) L
int handler::ha_write_row(uchar *buf)
1 H: w4 p3 b Q$ L ?{/ L; `0 s3 G3 X/ {0 k+ k W7 b
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })5 D* y9 v: b: H W' K" e1 O
}
8 F% `/ `1 |* O8 \. K: ]
7 N, `0 O1 z- c4 k) S$ A; u//这是一个虚方法
, R! Z2 N' _5 S" w+ G- h: S _( e/ ]2 svirtual int write_row(uchar *buf __attribute__((unused)))
% Q. L9 A. ^2 t6 ^{
3 [! ?- p1 C$ f) h return HA_ERR_WRONG_COMMAND;+ N. n1 a5 D! K; J
}
; Z* O# T7 G7 r0 V- D6 g' y, C: A8 j
9 _/ p% d* F6 g _9 d) {' X* q. d看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁* w4 y: L F: I. X1 @
- u& j, [1 C$ c; j
3. 调用链图, T2 C0 g! a( j5 q
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。/ [" J; u& E4 t. o+ e( A9 F" u
![]()
( ?3 ]& L/ k6 J8 i
: ]1 a6 e, f9 F a4 A" U* B0 [3 S: u" ]/ H! W& A( L) u9 }
三:总结( B$ H; w) P: K9 K: `- R
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。6 Y7 s9 C9 l8 V0 Z
————————————————0 l T! S4 [! P. U
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
! K* u/ j, p8 b. [( Q5 }# v6 K. P. _原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
' R2 t. v9 l. @6 Y |
zan
|