- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55570 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17622
- 相册
- 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 田老师国赛冲刺课 |
一:背景. r) ~2 O6 }" e% M$ c
1. 讲故事
# H4 e$ c' P9 X1 Q3 ?3 q t- B2 L. ~最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。1 n1 y4 ]4 }0 E) J: c# I4 r( ]6 h
, y& g, l _6 N% l
二:了解架构图# b8 Z$ }2 r" V. T5 z
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。7 o2 ?+ X& S9 t7 c6 o# O
' m+ V( R0 U( g7 l
1. 从架构图入手
& N0 q6 Y( ?9 V" f大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。# w4 a3 E# @" d
![]()
3 [' w7 W% d+ N6 V# W$ O' H8 F& n/ t5 N/ d2 O; \0 G
! J" C1 F, Y6 h% f/ b6 k* ^其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~4 W! k" j; y+ {' V- J+ B
# I% G5 @/ }& X9 u! `2. 功能点介绍3 A. G j# f) w% X# l
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
5 t+ I; m4 M9 v7 R* P R# B% W* T" t4 P2 U% w9 h4 |0 V0 W
<1> Client
% D* B3 I* U0 i% H$ k. p- s3 s不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
. y+ A! _ E5 z8 M( b9 S8 Q e2 U0 a2 C1 N, L1 r4 t3 x5 H
<2> Connection/Thread Pool
+ |, x- {! ?: w5 `MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
. l: g2 B+ f; y: y Q% |: a1 y @ r5 g8 T
<3> SqlInterface,Parse,Optimizer,Cache3 U( G- |6 A5 x& ^; F5 m
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
" F% e8 F% ^9 m
# z2 g6 C4 y( k _. I: ?8 F, a/ v<4> Storage Engines8 M/ t4 N! E+ F P2 J2 i) m
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。/ t) p o8 X- I7 p: G; X2 Z; R
3 e: U% | @8 I- |
三: 源码分析5 q5 N N! y; U R' y: I. ~, ~# q
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。9 M) G- I! Z' T( ?; V6 J
+ _8 ?& Z1 Q1 J. V: o9 P" C4 N- j
1. 了解mysql是如何启动监听的% ^. F( L2 ~* b" L# d1 K
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。7 \% X8 \, ]: g' `; J
![]()
6 b9 z: Z# m. `7 \4 |& C8 ?: w1 R3 ]; P3 X5 ^
3 _! C, e* c+ C+ s& T9 q从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
& S3 b% q2 R! ^/ ]6 l* `2 n- D% q% i1 _- b/ q. U9 U: a+ t+ U2 c0 S
<1> mysqld_main 入口函数 => sql/main.cc
8 z/ R% b5 d* \3 p( p
0 t0 t( F# t4 ]" _5 ^9 o
$ J" x. D. E- h/ o' K$ Pextern int mysqld_main(int argc, char **argv);
@ [) n0 h; @0 C: Q
+ s: _- U9 u, l# \% i g/ E! y6 {int main(int argc, char **argv)" J" a1 x7 h6 @. o$ ?8 L; U: h
{
/ h& h& H1 o& v6 a return mysqld_main(argc, argv);7 r. B# P, a% J* F6 P8 [* h# @5 q4 _" b
}
! }7 o0 y+ S7 I8 f0 L D. \: X$ K) I2 p/ ^3 T& L
! H6 q; W! b1 W7 ^7 Q这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
/ G; I; O8 } ?% {& I
6 [# j. T. X& L1 [<2> 创建监听
7 \6 c# J$ N% \+ B+ Z1 h" p7 ?4 b6 L* P2 ~8 O \2 k6 w
# l) c" _" e' T2 Zint mysqld_main(int argc, char **argv)
w9 P! d+ h9 u, [{/ b/ J/ J( e, E
//创建服务监听线程 Z0 y7 s0 V! A
handle_connections_sockets();
3 Z" }! \* z) D# @1 O}
0 H W* B6 F. u& y5 F* s/ c. |" Z
. X" U, f" C. N f) ?: a7 C- Evoid handle_connections_sockets()
+ g% e' d, x( M$ j) H{0 Q2 \/ H2 o2 v \6 t6 R
//监听连接
& v' t+ U( y4 R new_sock= mysql_socket_accept(key_socket_client_connection, sock,
5 O+ n J# O3 e8 J ~1 C& W+ z (struct sockaddr *)(&cAddr), &length);
1 O' p- S5 Z. U4 v# j" y4 \5 M5 M+ t+ _1 ?
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
4 T& B9 n# | e) p5 w thd->security_ctx->set_host((char*) my_localhost);
, q' v! M7 k" g+ I6 F) w, j# y8 H U& E" V$ c* Z# g0 |
//创建连接
+ n1 f! W& ~5 q' b6 X" J4 j2 E- G5 ? create_new_thread(thd);8 K% [2 R$ q" H, m5 [1 Y6 `
}
% @* y; @: ?# W1 Z$ @2 {7 V
& w0 h! T4 I* n( Q* Z6 O//创建新线程处理处理用户连接$ i9 t! }6 ^( Z* N
static void create_new_thread(THD *thd){
4 }2 w4 K3 a6 y4 O X1 W/ |& h2 g- p, h5 O6 ]6 M9 x3 i0 K
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;( w2 l7 o R! c' z$ d( @0 m- L
. ^8 Q4 P8 b- M$ k. l% X' T J //线程进了线程调度器
5 d- f, u; |( I% W MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); ( p. m- ?8 S4 j
}
) u% J1 ], x5 Y6 W& |) [3 S
7 S, ^. L2 V8 Q% K0 v3 M- ^# T# r$ B& w: q
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
* @; F; _! Y- I/ _ O/ L0 B" r1 U& L# G' [4 ?+ [0 o8 Z9 a* g
5 K6 Z0 S4 s5 N4 `6 C& s& ?
2. 理解mysql是如何处理sql请求
9 E& d L$ j2 d& C0 L: }这里我以Insert操作为例稍微解剖下处理流程:- t! B; a% g" v; q' b: q$ @5 A5 {
* _8 |+ Y0 a: S
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
; v' g. I/ w @) C d ~$ C
3 P- @8 t4 u& X0 N
4 i! k& N" x! T' ^) Qstatic scheduler_functions one_thread_per_connection_scheduler_functions=
) b* I" H3 {. W) j1 q" p{% i3 b4 i1 ]. K2 A l8 k# e
0, // max_threads6 c3 ?: q& x( P( Q0 j% \
NULL, // init
* `' E ^# ]% `/ |. S8 W/ q" L6 E init_new_connection_handler_thread, // init_new_connection_thread
* @+ _+ L1 F% Y# r3 l" M. Q+ O create_thread_to_handle_connection, // add_connection; a% h3 C$ A4 U" K, D" ~4 q1 O5 h3 d
NULL, // thd_wait_begin
/ R! B- f& W$ |3 C3 i NULL, // thd_wait_end9 z# m3 H7 P) n
NULL, // post_kill_notification2 A7 X/ {4 B$ m: C5 {
one_thread_per_connection_end, // end_thread
' t$ Y. o. j8 B NULL, // end
9 i- j, o% z! k. ^' @2 y5 ?- C& S: n};
0 n/ J/ N. c2 J: C! @' l4 K" H% m0 n- u$ j4 x) r+ {% u
- h7 r- J& @: E0 D' _从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。) W2 u( o# G- `
) H/ W" C: U/ `9 [% v
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪$ w; X1 b6 r3 J' G4 l
- J6 e+ [, R' s) G _5 @void create_thread_to_handle_connection(THD *thd)( {9 o5 _- x9 I/ Z- [
{
- g6 S5 L z; a$ _, z% g if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
+ |' {/ z& {3 F6 i- T handle_one_connection,(void*) thd))){}
: K5 M5 P5 p' N8 c% \}; G3 q/ V P+ O( b7 P$ V/ z
//触发回调函数 handle_one_connection' w5 g- w% g$ j7 j4 a
pthread_handler_t handle_one_connection(void *arg)2 }) r* W, T; g
{
; W8 h4 s9 e6 I/ x' Y* _ do_handle_one_connection(thd);& U/ m$ P* x* I( C* n0 L1 v8 g2 g
}
5 A8 E6 R2 w3 a1 r: x6 G//继续处理
0 Z& P% g$ f2 v3 D5 ]8 f. a) D. y4 evoid do_handle_one_connection(THD *thd_arg){ u) j% C- j( e
while (thd_is_connection_alive(thd))
: K$ q6 [4 ?9 ]1 C% | {/ K) W4 ]. G! P- x
mysql_audit_release(thd);
# a. d1 e" y3 \& h if (do_command(thd)) break; //这里的 do_command 继续处理- U9 ^& D' b+ i+ d0 ]
}) S% z1 C' t, [5 e$ Y# R
}7 _; n5 S& J* e0 q4 a% ?
//继续分发' L6 m& D2 B/ U. b3 d
bool do_command(THD *thd)! l1 @0 z( R4 m; h$ c+ d
{
5 X/ D1 Y: I: |% y: l4 p' v* |, E; ` return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
) b2 V9 z3 ?& z: `& B}
& V% u2 K9 ]% l0 ?; A0 ?, abool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)' U7 J# Q+ [# K. I3 k
{
& J0 D: ^ E, N# f$ s# _. Y3 p switch (command) {
$ U2 |3 r# g9 R" q case COM_INIT_DB: .... break;, z8 d# E1 A, f& ~
...0 n" O9 l: ?4 t3 a$ V# m
case COM_QUERY: //查询语句: insert xxxx
. x: p0 S9 m/ ~9 S6 N2 M mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
8 e. `) ^/ F' M( e8 u! m9 T break;
4 M8 g ^5 X" s# c! [& y }
! O1 R* h7 r% p& ]% ]}
5 P- q( |1 _0 `# x4 }( J- Y1 p//sql解析模块
$ X1 d5 ^% l4 U+ c) o( gvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)2 Z) f& c# i7 N( F9 W8 h+ s
{
* u; R& ?+ [, q error= mysql_execute_command(thd);0 v. Z5 Q! _6 ~' e, C, W7 ~
}
4 I) p7 O3 ?2 }' G
; k- M; `. `& t% B$ K9 c) ~8 ]: Q' H
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
' |- d! V/ {: B' F7 R |$ o1 V
# R, {7 i5 t6 u* t8 r//继续执行/ h2 s4 l. c; t0 u7 y
int mysql_execute_command(THD *thd)9 u* z x6 X! i2 N; a E
{1 J( {7 y3 L, B" F( j R6 {
switch (lex->sql_command) / C1 z, f) J# f7 @
{
# @5 J: D& r+ V9 J) S case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
; |+ Z. d3 k! e: x' [2 i! B C' r' c, h0 i) n1 f
//这个 insert 就是我要追的
) F( |" f5 g* w! U8 A case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
" y" q% ]3 S* {* H7 ]# @ lex->update_list, lex->value_list,
9 i0 V E. l" x1 I lex->duplicates, lex->ignore);0 e' ?0 s( \# X; \9 D: `! s( ~* E
}) U$ P7 D7 h! y+ V$ K3 ?5 l# W
}
# |& \% V; v. q3 `//insert插入操作处理
8 R! k- d4 w4 f) p% k% K a8 abool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
3 k8 T# C1 U6 {% a% | List<Item> &update_fields, List<Item> &update_values, & T$ f& r+ X6 k3 I
enum_duplicates duplic, bool ignore)
/ }5 d" M1 o" N9 G! @7 |{2 n4 @( s. H3 k# H# t {$ @/ j
while ((values= its++))- f! {5 r. w2 d) q' |' L/ {
{0 X4 l% V7 E' Y1 z/ |
error= write_record(thd, table, &info, &update);
! {$ t( T# x+ y }
$ d' x$ y, c8 n}1 s$ V3 L2 K; p1 Q
//写入记录
4 {, Y8 g% l9 S1 X$ G& e$ Y' oint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)7 q2 j# Y, U" s, ~! @9 I
{
" D# F6 z P1 u/ L$ I# i" [6 s if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
0 n* U5 l" l' `( t" Y6 b {) w- f- h/ ]9 S
// ha_write_row 重点是这个函数
( s( h5 |$ ?$ V* a* `% A, n5 L; |* | while ((error=table->file->ha_write_row(table->record[0])))
: Q q1 o& K2 E F& l l {
# \4 `) C [- f8 g- r- n ....0 @1 b; Z# a6 N/ k' H m
}
( v: x$ i5 I$ [; u4 g }
+ @* T: y( v( y# X! I% z A}
3 U# @ K- E0 p- _) t/ H: S4 z; u% ?, T: E) R7 N
! B- M% S2 x6 I# T Q& l' i& | o( q& u, W3 f, F6 p
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。" |& X% B+ ] p6 p/ `; u
( `2 u g X6 H) R
<3> 继续挖 ha_write_row5 P5 t& t' W, k2 a1 y/ O
H d ?9 w8 Q7 a
int handler::ha_write_row(uchar *buf)
) X* o, z% m; B{$ x8 A2 ^5 _0 F1 c0 j0 I$ Y8 c
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); }). i& A9 H6 _. g; \6 w
}
' M5 v: Y3 b% t7 ]: Z# P0 q. U* [; S K" I% ^# o# T
//这是一个虚方法
, `8 F( q" G6 D$ m" a* ivirtual int write_row(uchar *buf __attribute__((unused)))$ h6 z: E$ _1 l& _3 O# t3 B3 s
{; E1 U& t& V; A, ^& C9 Y" a" s
return HA_ERR_WRONG_COMMAND; C6 j& z4 A* a$ S. e6 }" S# I
}, L3 H7 y, I& H! U' {4 F
2 |/ L1 F$ Q6 E1 `+ j0 N# i- i
9 F- H! G1 `) x; }2 t p5 M
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁' r2 v$ y, o* Q. l
) g" y' ?1 d+ t x0 E
3. 调用链图
4 d9 W3 Q( {/ i7 n这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。3 S; x4 P8 S- i2 Y1 v6 t# `! }8 B
![]()
* z4 P# c, n0 w' k0 v& B+ C/ ~" G1 T8 Z4 H9 T& E4 E) a3 g
+ s% y; m3 z5 ?5 j& p) N9 X三:总结
$ m; N0 Z& \1 N5 Q) s0 Z& N% v& a大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
1 B: m5 Y& `; K% Z. P( P) i/ H————————————————
; I9 A+ S3 G6 {, r+ H版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
1 Z K9 M& a4 c* i n, n原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415! x/ [$ [9 O4 X
|
zan
|