- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55556 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17618
- 相册
- 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 田老师国赛冲刺课 |
一:背景
: }, q* o1 H3 }2 h# Q5 w! \9 N9 {9 b1. 讲故事, G- t; U4 [, Z) v: h
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
: r# |$ d, j% {) V5 P4 a6 ~
& b! q7 B( H2 Y5 T3 p' N二:了解架构图
; a6 S* {6 O1 ]mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# D) T5 p, i& i! m* p+ T5 m* p
, j5 P t8 ]! M* k5 M) V
1. 从架构图入手6 @, u8 ^. ]) k* q/ F4 p
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
' e5 g: b3 K" ]4 S , z7 q& q# M4 Z5 Z$ A' p0 f
1 T' Z; u' s9 S c! D* ~* N. r6 G) k6 y) i3 j
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~. Y: Z) Z. }' C' c5 b
, x t) e, b* |' h; D S9 }' z2. 功能点介绍
4 ~9 a" I9 C0 m- b; T& {MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。+ H+ X" S7 b* v$ m' b/ u9 E6 u1 I
8 A/ T5 g% i+ q$ f( q& Y- q<1> Client, I* v/ z$ M0 ]) |% D. n9 @
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。% z& [( z9 g% j- r
0 ~; _" @& i, d8 W6 S
<2> Connection/Thread Pool
/ }$ i, |, A: c9 b0 J+ h qMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。6 u, J: L; j* u7 E4 H1 `7 X. w# q
$ n4 [9 N! D* Q% k! h% [
<3> SqlInterface,Parse,Optimizer,Cache
( ?9 `9 \1 X. U/ b0 k对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
1 F& I( p, S& L" }
8 s! v8 k; T+ I<4> Storage Engines$ O: g% e! S6 B5 Q! O
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。0 e* ]5 O2 @1 L' S
' Q a3 c% `* d/ _- I X三: 源码分析
1 P' O$ V `6 M6 Y( Z关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
7 n0 T6 E. A& X Q* k6 J
) l: b o3 f" c4 f7 G" a% _1. 了解mysql是如何启动监听的
/ J, e, \( t9 V手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。3 E4 U' B! g1 j8 i/ [( T
0 Y( N1 _. M+ J: j
6 u6 b+ w( C4 |# O3 O% y- h9 m& D- l" k9 ]8 T, y9 U
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。/ H7 P! C" }( J$ R
' p/ f( ?0 z2 c/ ^/ h$ q, ]! C% y
<1> mysqld_main 入口函数 => sql/main.cc0 l7 W7 t- g, b5 W' ]$ L
; q$ J/ r/ {1 v1 S. s
3 Q. ]3 _/ Z+ e; H& h
extern int mysqld_main(int argc, char **argv);
3 y% ]6 y3 A7 k, {/ E; v% ?8 v# E" i" U3 Z' l) l. a
int main(int argc, char **argv)( q( X. x# b8 l+ m+ [ L6 t) v
{
5 B; u' D4 j3 e7 T9 {4 f return mysqld_main(argc, argv);
( d) ]9 ?$ C/ Q: @' }# h/ N}
: n+ u" N' a3 I. {9 ~& b( g
v4 F, F- P6 t- [9 Z# C" Q
" g- f7 V0 g" S# n这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
9 m/ w; W+ j+ r3 h& b& [6 |+ E* }. l" ?; h* b1 P8 v: w; _$ F
<2> 创建监听
' O* F3 w* `' Q. ^& f1 Z4 e. G
& I. j! _+ j, ^
0 e* S7 ^" E# T5 @5 i vint mysqld_main(int argc, char **argv)
3 Q: M w, ?) I9 S F* j{
5 V6 H9 Z7 d" A9 v- L+ O //创建服务监听线程1 }0 Z, x1 q" U
handle_connections_sockets();
$ T% x- Z! f8 K1 E; I. |7 g1 a6 v}
9 e2 }4 ^+ Q- m# S2 I- a- [5 v" z( c9 L% y2 m) x8 |1 O- }
void handle_connections_sockets(). d; n4 ?2 w) G9 E9 r9 B& Y
{
6 m, }7 b: u/ I( d3 J: ?% ~ K //监听连接
4 N9 T1 k/ f" f( e new_sock= mysql_socket_accept(key_socket_client_connection, sock,
! Q9 Q! s# D% U6 Q) p6 P$ _ (struct sockaddr *)(&cAddr), &length);* O- L) L$ ]$ R3 F5 \2 f
/ O! O: H" I t# }& r
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))3 Y; E% M6 S5 n; S: H+ P. l
thd->security_ctx->set_host((char*) my_localhost);
# ?; z) c# }4 q8 F/ U- z. }' {4 C. B& C
//创建连接# j# j- E' Y# N4 C: p* k
create_new_thread(thd);7 {7 L8 d& A( I6 n
}: ?2 `* D9 T$ d% B5 l) S! a
, @: q7 o4 J% y2 _; M2 E/ g& c# Q0 d
//创建新线程处理处理用户连接% M+ k3 N' A2 z [
static void create_new_thread(THD *thd){ h, J$ @( ^" V2 m9 M4 r$ u
% d2 I+ Q& H; J/ W
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
% }+ [, q h/ D2 A4 {& U# G8 E$ a) k! H; S
//线程进了线程调度器+ g, W: k4 C, S
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
4 t6 L: X9 R7 Y3 J6 L}
# \* a. v- Q# t8 V9 r$ O- H1 T7 N. l7 U. b0 l
+ w6 S$ u7 \ U1 q至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
" v6 M) u; f6 h5 T9 b" u" o1 p) G9 \& T
7 c+ E) g; e6 Y. i) j" N! X
2. 理解mysql是如何处理sql请求
. j: F+ P4 f' ~这里我以Insert操作为例稍微解剖下处理流程:
( E# E& S8 I2 }
* t: v) U% X0 B4 R* F8 f+ ^6 v当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。8 }8 _2 h/ _4 K" X) \6 Z
0 ]! o, g7 j/ M& W7 U/ w) Q" A
! P9 s1 Z9 c- }% F* Z) Mstatic scheduler_functions one_thread_per_connection_scheduler_functions=- ?2 Q) Z' C, n0 a4 Q: L F: g& K
{
6 {: u5 A& s2 }) N 0, // max_threads
9 r/ D) }' F6 _8 f NULL, // init
- f" ?! m4 W: K/ v init_new_connection_handler_thread, // init_new_connection_thread1 u9 [) ^7 F" w" F
create_thread_to_handle_connection, // add_connection
$ B8 N6 a5 h8 D3 i+ W NULL, // thd_wait_begin
( R+ ?; {, A( a NULL, // thd_wait_end0 h7 @. Z/ V4 d' b# F; u0 p0 x! U
NULL, // post_kill_notification
* U/ L4 L( ~' {7 A" |9 `/ c0 S one_thread_per_connection_end, // end_thread
8 e' Q2 I O# B+ L7 G NULL, // end
8 ?; k4 ~6 E, F0 ?/ p& c1 s};
. N4 \9 `. R! f$ g7 z
4 O4 W' ]: m% y- K
" z0 E, ]1 |: ?! i% W" H2 ?从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。) ^7 ?# D U( u* N4 D
' _; C8 {" ]! G: q% C" A3 m6 b<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪& p: P+ h k) L7 ?. g
: O1 W1 e" W- T! Y' R
void create_thread_to_handle_connection(THD *thd)
* t4 w1 {1 j r$ C# h{4 L' {5 e( s/ Z' u
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,# D3 Y! O4 y6 s% H" @
handle_one_connection,(void*) thd))){}
! r. D8 F; d& Z' G}
1 R9 Q9 E' a8 Q. G: T, a//触发回调函数 handle_one_connection
1 m% H7 M; h9 spthread_handler_t handle_one_connection(void *arg)
5 o1 f4 m, S) _3 _+ |) ^! \" ~{2 o( ?% ]4 ^/ @: Q9 _
do_handle_one_connection(thd);$ k. V& l0 {4 L* D! f
}( o+ U' J2 X6 n4 k( ]1 M( \
//继续处理
! u- O% _7 T2 ~' Xvoid do_handle_one_connection(THD *thd_arg){, Q5 h; c* p' g3 L& @; A
while (thd_is_connection_alive(thd))
% h8 d( u# v9 D {
O( R4 m1 f3 E7 n' g* i6 U mysql_audit_release(thd);% W) z% V7 E: W5 w+ b
if (do_command(thd)) break; //这里的 do_command 继续处理) l' W1 x g, ~8 o
}
% p% h1 W, L/ Y% F2 l' ^}
) R4 j% L- x' F1 u9 u4 o//继续分发" J2 ?0 X9 ]: |2 S5 }+ {3 t, V& S
bool do_command(THD *thd)
+ V$ C, p; N6 l{
& G5 n, i' p5 A! W f8 @3 \4 I return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
2 q) N9 i. h+ F2 T q/ [6 f}% s* b' ]; A! }) E1 k' S$ \ W
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
; N, W: Z4 j- j7 v; R& E* v6 H# }{
/ H, ]. j; l; Q$ q. D# ] switch (command) { K c, s' M5 [" o9 n: C
case COM_INIT_DB: .... break;
- P4 K" A* G: S9 L ...7 {3 ^2 J* ~) l) t
case COM_QUERY: //查询语句: insert xxxx
0 n8 x& p( @- |( P. F* J" n g! z mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析" z% u( ?2 a) F( a$ l; `
break;! n+ L8 [& _$ ?# T1 [; ?
}5 b( `* C0 F+ [/ a' C
}
0 d8 D7 ]/ y2 {//sql解析模块* ~9 a' z+ A, [# Z. ~' Y: `
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
& b; J, ?4 u# H Z5 a! ^{
3 ?$ U, x, a/ \$ U Y error= mysql_execute_command(thd);' ^5 `/ B# {# a2 d( F
}
/ E0 Q, C/ N* `- v4 b1 q0 [" g9 w7 K% [
8 u" C+ Q- ]4 D- Q( r
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
* k) C7 |3 y" `8 @+ a3 n: l) p: ~ p$ H) z) I
//继续执行/ N% ?, j t3 q3 w; A4 B% J7 V2 Q
int mysql_execute_command(THD *thd)
3 b* w- W* |. V) F& t{
. S, G( \' ^3 c" E% C+ w switch (lex->sql_command) 5 x. {/ \$ i) U! w; {
{
" O+ q% l8 n# I7 D case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;, w% h" R1 }; H
: x6 V; \ T4 ^+ p- F
//这个 insert 就是我要追的
0 Z9 q. `$ k4 L7 b1 A$ b, y" t" J case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
2 {4 i- D7 g, p9 Q! Z: A lex->update_list, lex->value_list,
?6 l2 O7 a: Q3 } lex->duplicates, lex->ignore);
3 p4 s5 @+ ^# b) L- k% w$ E# _( b }
3 J2 N! S- S( y, [" r}6 `3 N2 i! T) @
//insert插入操作处理/ O7 ?8 N2 b. C; a
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
4 X$ \" |3 n9 e! z4 ` List<Item> &update_fields, List<Item> &update_values, , T) F0 Q7 C8 R3 v/ w2 j
enum_duplicates duplic, bool ignore)
- |3 q6 U2 l' o{
5 [) O) J0 \; a6 S8 w- f; \4 i while ((values= its++))4 a( D* y5 r2 @$ s& X4 h/ V; a& E
{
! m9 @2 Y. {" t error= write_record(thd, table, &info, &update);
1 w% P! b: Q* H" B6 a! e }' G5 {3 W( P0 _# N2 ]
}2 d$ J4 E1 a2 F0 I9 j
//写入记录
( H( o4 W& |1 H5 Z l# Z5 @int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
; B* F( f, Q0 I1 L8 S$ e{; w$ `% b; n5 @- q6 t
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)6 [2 ? J& T+ t- a N F" m9 `. N
{
5 D9 @% r8 N" P0 H // ha_write_row 重点是这个函数4 q8 U* P* C r- N
while ((error=table->file->ha_write_row(table->record[0])))" k$ e2 a* R! f2 S9 l7 {
{
, O+ j$ l5 f$ `7 V. L( m ....# ?8 z) ~/ u) }1 c
}/ U" b, j. }2 ] K
}
' p- _ Y1 X6 l8 b! p8 ~/ w! ?}
* d; u" b# R! g3 C* S& t
0 R4 X" L, Z- r& L1 z4 q) H( C6 p7 u2 R) v5 _5 j0 i- n' k
5 i' h: T4 R; O1 S$ f可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
7 `: T5 b/ V8 {+ Q. l" i$ U+ H$ y# v( _2 Y$ ^( h
<3> 继续挖 ha_write_row
3 o" A/ G. p; T$ ]8 [7 [, r6 D- c$ S% B
int handler::ha_write_row(uchar *buf)4 G$ T5 {! O* \
{
6 c1 k3 F6 F7 `5 a MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })% L9 D/ F, L2 t
}
. D1 Q0 b0 V+ V. b+ }7 }4 @& t3 {% G( g+ H
% H% S: ~0 f! D9 L" W' O//这是一个虚方法/ O2 {5 g+ x. Y) Q$ c5 |0 U
virtual int write_row(uchar *buf __attribute__((unused)))
* {; f8 {# Z# g) m( d{
7 B$ Z: O/ H; ^ return HA_ERR_WRONG_COMMAND;
$ s# t6 Z+ C+ A2 u* _! v}
! Z2 b8 C% P: }( v1 J/ I# p e3 {9 Z& d8 l$ @1 ~& }
8 e* r! l5 m8 F3 |; P# d+ ~+ e1 d9 m
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
: p7 {4 S9 B1 Z" M+ z" S
1 X8 q( a3 W. m% A7 }# I% Z; U3. 调用链图; s! T5 r) P- B9 d
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。/ C& V( b! u8 m( ~
![]()
+ W" d; u' ~4 i; e% @! r) v
3 |6 H* o/ T0 b8 y0 _" [. t4 J& L) R% ^; g% k
三:总结$ n2 L) S* w/ P& Y7 \5 X; p+ ]
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。& g7 Z# g. S* @& l' `
————————————————
6 ]6 y+ H z9 p/ M, y/ u版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
2 @! {' X) y6 X. K" O: v% M原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
* v% h6 E6 Q& B |
zan
|