- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55541 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17614
- 相册
- 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 田老师国赛冲刺课 |
一:背景
$ i. t$ c$ {: m! U( Y1. 讲故事
& M( N( z m! I% Q) K1 s最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。7 |( @. t( ?* O7 H+ E# O1 `3 E
) x$ M0 W h; o. I. P1 j8 s
二:了解架构图! O6 s/ u: m6 z* m$ n& F4 s
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。. j$ T0 F* h) _7 b- |. k4 W
7 Y. H: E' T& V2 ?5 g1. 从架构图入手& _0 h3 x8 G% K* @
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。# }7 ~5 c7 O# r
( e7 t, s/ [7 E( ^
b( ]- b6 ]9 @1 l9 E! A
X7 |6 K8 u' X1 H- e
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~$ v( G* Z: X4 x2 T' f$ u6 R2 k
; C( b! Z3 `8 O! `6 d4 p4 c2. 功能点介绍# r, Z4 p" }1 u9 P# `7 h' C
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
3 M" T7 J" j( i7 F
) e5 Z' Y4 c8 `8 w<1> Client2 Y7 K8 M, p) B, t7 v9 K4 s* h
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。6 `# v6 d$ T* O6 f
7 O9 e5 n' Z4 e9 f" K P
<2> Connection/Thread Pool
+ `9 Y3 {$ W7 K6 F! xMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。8 F! B4 h, l4 _; {$ x7 y/ D4 d& q& Z
: w/ D6 J! q& A5 Q<3> SqlInterface,Parse,Optimizer,Cache
' B/ K4 ^3 ~2 h' ^& a' ] @对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
7 c1 G# @! ~; c3 O9 t1 X8 M. x w& ^ X7 W, g6 Z
<4> Storage Engines
! s: X+ H; {# W* [3 y! q- E U负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
: N: Y( D0 a; v3 }/ }' t
# D& U/ C( K1 y三: 源码分析- P$ u3 Z& L6 A, S/ z* O, _
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。4 e7 I* E F- D- |; i9 M
- q, V: J- s4 q0 j# l" [1 n
1. 了解mysql是如何启动监听的
}2 F* k* I0 B& b9 I手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
) @- n* Y! O# K( d+ M8 j3 b k 8 e3 l: u6 z( `, J0 f: H3 s, ^
; k3 {4 W! o6 x! w- R# q( @& ~
$ N( k" a/ }/ m, |3 c5 L. X* j( A
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
* V1 N+ a% R8 [3 T7 i- R ` P0 c" d' D
<1> mysqld_main 入口函数 => sql/main.cc
1 r+ ?; K8 O" w" J% Z' I+ Z2 ^3 L# O+ M! l( d' @
- S0 W9 x3 Z; J3 `extern int mysqld_main(int argc, char **argv);
4 L/ M! P; g- l
9 l7 Z4 ^4 G7 K: Yint main(int argc, char **argv): C8 \( J, c, b: f A
{
% ]9 h; D# ~5 y8 @3 V, X9 Y. M& l return mysqld_main(argc, argv);
5 s: k9 e, Q% D6 Z}
, l9 Y' M1 B T# d' n7 F( m$ u/ h, @4 r5 V- E
; a0 f, n6 d- `0 `/ V9 [" }
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
5 H# u, H/ j. Y
; C2 O& H/ Z& H# Q; I x<2> 创建监听1 q2 b6 }+ W# J4 C* F9 x
: y. R+ ] U( f! `- A; x
7 o* L& J3 E9 `) P% D3 }& Xint mysqld_main(int argc, char **argv)
" ~+ c9 K0 a5 y; u {4 x{1 j @0 v$ x, g2 M; B1 J
//创建服务监听线程
* ^3 E- X0 @5 @$ |3 ]( s- H handle_connections_sockets();
3 I/ C: U9 M/ `! ^}- d3 e# ^6 t3 {2 }/ }
f" t* W; ~ v$ Q0 T+ evoid handle_connections_sockets()
9 H& t/ K- b; k. Y. {6 d2 o9 W0 h$ s{
- h) w _5 n3 s% i. t' O. G" ~5 v7 Z //监听连接4 B8 w% f5 Y- x1 Q* I' x; g0 K
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
* i; U: [3 N" x" a; f5 h/ V: w* O (struct sockaddr *)(&cAddr), &length);6 f+ y8 i& \# E: A# T0 P; n
9 e1 \( @1 `) a, y" d( a if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))& I. T' ]* d8 B8 `* }
thd->security_ctx->set_host((char*) my_localhost);
5 ^8 s1 N }; A5 k* {4 t# I; u( H3 ^, ?
; Z) v0 ^* F7 f6 _ //创建连接6 A5 C$ U4 g" u2 D: ~
create_new_thread(thd);0 S B& R/ D4 I% t/ l
}
# c: a/ v# O; q$ A2 \$ S" p* e/ ^8 I! X* F! p$ W4 Y6 M s) v( y
//创建新线程处理处理用户连接/ S/ ?: H9 d+ P0 W1 j- z
static void create_new_thread(THD *thd){
9 }# B+ v( G, s4 N6 K8 f. ^
* i; h- A1 k( X) U3 M; n thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
( x- M- f* ]% M; z6 s0 @9 i2 D5 a, T$ u2 p9 H" u; n- S
//线程进了线程调度器
' g& ?5 J0 t. Z% t: F; S3 t MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
' d5 S" ]2 }6 h1 e6 L}
! z: W, g, t+ d
% ]" e) H6 {: G6 y/ P
2 ~, l4 @0 Z0 C至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。1 k- p8 W7 u4 S
2 M5 R; g# f# s9 o
/ J7 C! z8 U9 ]) ^5 W Y: D" G! h3 u2. 理解mysql是如何处理sql请求
: [4 w0 X, p- f/ B! r! @9 d' H这里我以Insert操作为例稍微解剖下处理流程:2 R9 n9 q) n. H8 I7 F$ t
" [- A9 t; Z, \. ]# R: ^1 e G
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
) Y1 N; z6 x* C0 u. d
) Z' n0 v' B9 W* u9 a
9 I7 j7 P) m7 g! e8 w' ^static scheduler_functions one_thread_per_connection_scheduler_functions=: K/ f" [+ ^( T" f% E* E
{* x9 |+ z; U p2 f7 o
0, // max_threads
! B1 a) A* ?7 `+ f0 H. }1 b NULL, // init: c1 u; e# J( {$ K
init_new_connection_handler_thread, // init_new_connection_thread
- d0 S8 O8 ^5 ]' v create_thread_to_handle_connection, // add_connection
5 g2 \* f. `6 v# x' W9 T% d% a2 T NULL, // thd_wait_begin
* n3 U' v1 j& d7 Z7 B$ t NULL, // thd_wait_end
& V& |8 L2 r: I* u% L; A0 B NULL, // post_kill_notification2 I- h3 W2 d" X% D" _8 K
one_thread_per_connection_end, // end_thread
4 ? @& R* [$ V2 B# `+ J/ l, s NULL, // end% y2 v) A1 I+ R3 A( S
};7 F0 M) E" K/ T5 ]! c
9 N3 ^5 x% L3 n1 _- }
! N, _; F7 J; L9 Y3 ^+ V) ~0 D2 e
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。) [- i* u* d( h! c; |
/ i4 g0 A! |' k/ T9 ^3 y4 t& C<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
+ {, H. P4 L* ~& _$ {4 r, [; ~ o: I$ \# ^& x9 Q; l
void create_thread_to_handle_connection(THD *thd)' ?1 _0 x# D1 R+ T W+ k
{# q4 [3 l% W% d
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
, Y, F' W. e3 N handle_one_connection,(void*) thd))){}
0 {' T, X9 I$ B- w# A, V: T}
4 a, \& _2 ?1 u6 v2 B) I//触发回调函数 handle_one_connection4 G& l& E8 W+ V/ F6 }6 c0 x/ Q) [
pthread_handler_t handle_one_connection(void *arg)5 N7 t) Z1 f' x7 b, j5 X
{
! b0 |9 A. l" H& U2 q2 a. a) F! E do_handle_one_connection(thd);
1 z& z/ f; h2 k2 J}8 Z* m4 j( a+ U* U& ]2 L! S
//继续处理
4 W+ r, S8 @* R* E" Bvoid do_handle_one_connection(THD *thd_arg){! p3 I4 V4 p( Q; {* [7 r$ N
while (thd_is_connection_alive(thd))
! m' L% U* d& L) u% s( ^& x {0 r9 U6 [3 O& ~3 Z+ {
mysql_audit_release(thd);$ f v# Z% `& {( o
if (do_command(thd)) break; //这里的 do_command 继续处理% N6 d$ Q, v, g$ a/ L/ V' \
}
* ?% F0 a+ V$ i; P}
: _2 v8 S* {* R3 p7 [+ |5 B5 |//继续分发
8 r2 @5 z8 D6 H3 W- v1 ]bool do_command(THD *thd)8 l. {; q- {0 v7 l2 @4 k+ J, w1 |
{" r; a5 l4 B% i! V
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
# [. S% h( q" t/ l D4 v}3 ^% ?0 H' b- a' G$ J
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)- J3 q: c! T2 s) [/ M
{ M P( K% f* C7 u% {
switch (command) {" e5 z: t: }& g7 z0 _! P$ l
case COM_INIT_DB: .... break;# {7 d6 W5 N9 i$ `& }) Y% i
...
3 f d# O3 t5 n case COM_QUERY: //查询语句: insert xxxx
' }; ~* V1 N) S; O+ O9 u mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
2 X; z3 J R" f% U4 F break;
& B/ ?2 K& r5 c! [; b9 l }; w4 S' h& P; U+ x% [% }: r3 F
}3 @! P C }, p
//sql解析模块5 d$ _# a+ O a& |
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)# V4 L3 i. J; o* u9 N! P
{
- \; l1 w8 y' s: `2 O- x4 Q* \ error= mysql_execute_command(thd);
: s- X/ v5 [% k2 d% }, {}
7 u3 e- I# S1 {" J$ j k3 c8 `/ H; [( G' v! O, X
( n& K* }8 X( ^! C" E
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
) X7 @0 I( C, Z4 f4 |' d
; T4 F) a# `. `+ y8 q( \' V" W//继续执行3 S8 V0 w- Q7 h1 @9 _" u0 Q4 i
int mysql_execute_command(THD *thd)
1 w9 R H, N. M{% K: G4 e4 i8 @# _2 u
switch (lex->sql_command)
9 z/ F$ s W0 ^( g. Q' U {
1 n/ G# S! g. ?5 N& T% l. r case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
/ @$ }" B6 p3 H$ c3 C/ A- e, w
//这个 insert 就是我要追的
5 A) m9 ?: }( J9 J: u4 R2 \9 F case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,# P3 E# I* }" P+ {1 t# c, _
lex->update_list, lex->value_list,
/ B. g. H) t3 |6 m& ]: y" c) J lex->duplicates, lex->ignore);5 C# [ B& |* i/ G# v- I! M+ [
}
$ _0 q! f% H. @& _}
8 R- w8 k. [- \! D; H//insert插入操作处理* }1 O0 A1 Q$ u( g7 w" W# P' x
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,$ |$ x$ ]1 S1 Z
List<Item> &update_fields, List<Item> &update_values, 2 m# @# \. ]" d3 z) A4 Y' T
enum_duplicates duplic, bool ignore)
4 S. g7 H- L: G: b{
: c6 N3 b/ c" P1 R1 S. u while ((values= its++))
F; J' U4 T! a6 ] {
" Q+ T3 C, m- L5 b error= write_record(thd, table, &info, &update);
: }2 n& E! P* {' v- k& q }
, U1 h' n# I1 a/ N}
- w+ S4 a6 k" @, Z//写入记录
: ~# j7 p: g" @ _# q% }5 Z# bint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)8 q8 Z/ Y5 u I& Z! g
{
( ~) B( ~, y/ L7 B2 ]1 H if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
- S% @+ N9 c6 R1 m8 m& ?; w0 O {' m/ t, b( e/ j0 e
// ha_write_row 重点是这个函数7 z% W3 O" C& o6 U0 f R3 m
while ((error=table->file->ha_write_row(table->record[0])))+ |: c. M3 k' n+ M( _1 l
{
% I" @ O+ {5 h) T$ J+ \1 j ....) d7 W; u# e p. `
}
! O" _- p: x! j( E- H) t }
6 ^! F7 W) X+ U- p: I" |}
$ p; b6 K. E' v0 q" M* }& y- C& M& R/ X; L5 `& x( _. Z
* ?; I7 o% G) n$ I7 g5 B
|0 S- }, ^- x3 |& p; T8 m0 T可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
; b7 @9 y6 Q! z9 u! M/ a: j8 ]7 k2 F4 P1 [ p6 G
<3> 继续挖 ha_write_row: a4 ^; r0 D4 q8 j4 u
: h$ t) E5 x- l! wint handler::ha_write_row(uchar *buf) V' B5 O7 |8 J
{
1 [- b" M( x" L. _ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })3 I% t3 \( t/ N, @
}+ L8 j3 A$ u6 j2 t+ R' P
; C* p( o7 A- C, {+ S1 }! {
//这是一个虚方法
3 [) K# G7 E+ dvirtual int write_row(uchar *buf __attribute__((unused)))
3 M1 ~- V& K3 }* s4 i{/ ? c* \) P3 e5 ?) z2 k' l: _$ W L
return HA_ERR_WRONG_COMMAND;! ^3 i+ [* I8 R/ v. K. U6 i# C
}+ F! q* W: R( U, _5 C3 ^' U( X0 {
5 a7 O* [8 V6 p* @" L# @7 d# o5 e8 s8 F7 j7 ]0 g' r
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁" x& a4 E- w0 k3 f0 m0 q
$ p) G" J) w) `; @2 I. y& i2 e H
3. 调用链图
* {7 q- H4 `' n4 {! a# ^- A这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。2 |7 `) j4 a" K, c
5 b& F' D7 E% F$ s& B: `! I t
4 N. l1 P( @" h P
8 U+ c4 G5 k1 c: M2 c" I% e: v* L
三:总结# x7 R5 m, m; l7 o
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
+ W& u" ]; u7 ^# @————————————————
% @3 H+ z, a/ M6 S5 X: Z& R0 C版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 m% N. e- H! [$ Q
原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874151 e# a+ K5 S( E# q; p
|
zan
|