- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55580 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17625
- 相册
- 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 田老师国赛冲刺课 |
一:背景! ^; C2 W/ |; W3 t7 s5 {1 y4 [1 U
1. 讲故事
3 [7 _+ }, a* s9 m1 c最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
* p$ G8 `& {$ P2 I& x+ t2 m; i" z0 L; K+ ?5 ]. G" Y2 a
二:了解架构图/ T) h; l; N" e. {! `
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。' f& M/ F: y/ Y* C
4 D4 w/ | }& ^ s# i
1. 从架构图入手
- z! f" |! @; h7 b$ o大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
/ o, d0 r `# G7 a- w![]()
7 ?0 E) V8 L: d& U. D+ S n+ ]: Y5 h, a X9 f4 {: n e( z
- R0 |! V A; P( j! n" g1 ~
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
5 ?, e- l# c& l8 x7 I3 Y0 w* z4 K+ o) X1 `
2. 功能点介绍( C. h6 Y+ e" [
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
" Q, l/ _# z$ v4 l. c: C
) g8 R/ @# {# L" u<1> Client K+ A0 I* K# Y* W+ }2 X
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。 k+ r; x: f* t. X
- a/ g. v1 Y' R% b; ~9 V, W9 e
<2> Connection/Thread Pool' q" W! M& P( A, ]' a
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
( g8 X1 @/ \6 X5 {
( b! D/ X! e* X<3> SqlInterface,Parse,Optimizer,Cache
2 M$ [ j* }3 Q0 U4 |1 c5 W对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
+ s5 `6 A2 L6 D* ~8 U# j( q! y: k8 h$ r4 R# Z3 A/ P+ f
<4> Storage Engines% R( S# C3 S/ Q5 f j" _! V8 z
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。) F# R# o9 o, e
: v9 S0 `) f }7 s+ M Y+ P三: 源码分析6 x1 ~* k* O9 f1 B, _, i5 S/ q# N9 r- k
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
1 h+ d9 J5 X" H
1 M6 q K. C( T0 Q9 b! @1. 了解mysql是如何启动监听的
% e- _% ]! ?! B手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
9 b& |" v# P" H4 w1 F1 A![]()
) ~ n9 f3 C6 _, H, d k, y8 m
5 ^8 F& a: w9 O: k0 j; I+ Z, f8 c$ [; P ?( R7 r! a
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。4 ~+ i' i; @' ?7 M0 M" R) g
/ H; T$ r! L7 T9 b" p
<1> mysqld_main 入口函数 => sql/main.cc
8 t) U( q& ~7 m4 E0 j o
# {; U* m3 m: j& H0 O- X* j/ g! D1 @( g7 ^# v2 y# S
extern int mysqld_main(int argc, char **argv);
7 \& J& y5 p/ _2 ?2 p
. }+ T; [# m' s6 o9 n, f c6 v7 dint main(int argc, char **argv)+ @- w5 _/ K: u' G$ ?& N9 [2 U/ S; n
{
- E1 v8 F+ I9 p$ {& { return mysqld_main(argc, argv);) w3 G) y! a0 B/ k$ w3 d. S2 _( ~" k
}* y4 Q% r2 r) f5 y
1 n* J0 K+ M- i" L: t/ i
( y8 A+ J8 S- n K& I4 J
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
+ y, T7 u6 a. ~* w% S
9 K9 ^6 f/ X& ]# V' P6 S) z |: j<2> 创建监听5 S' L& B! w9 N
0 {/ g( v- H5 {+ B1 g. y L
% c/ @, l5 Y/ L) iint mysqld_main(int argc, char **argv)1 ^ y: k( {' i. O
{4 F5 e! T$ N) @8 h$ ^/ ~
//创建服务监听线程
3 L: Z/ t# I) f handle_connections_sockets();
) M# |' ]0 a+ v% w4 d: ~7 B$ C" ]}4 R5 E: J# e; G9 x$ s
9 Z1 h1 {: _# p7 M7 Svoid handle_connections_sockets()/ }6 _/ A* [. f
{/ C- A' T! e" _. s9 H' M
//监听连接5 P* z$ R+ F4 l
new_sock= mysql_socket_accept(key_socket_client_connection, sock,0 @' [2 e* _! W3 e
(struct sockaddr *)(&cAddr), &length);3 @+ {9 }+ ]9 F! r1 h% |$ d @
0 b4 x' P N- Z5 u: { if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
$ X5 n3 ? D4 p thd->security_ctx->set_host((char*) my_localhost);
/ ~0 e$ t, B% d8 z- o0 n# j. T: z9 B$ o
//创建连接' h6 n$ j+ M7 b' O: o
create_new_thread(thd);! B4 |) v- D0 ?8 R% y% n
}1 Y( N- q) A$ ?- C4 {4 o
% ^, _3 g8 z' S6 [' U! {- z4 p9 y//创建新线程处理处理用户连接' C4 w8 ^3 H5 e6 x
static void create_new_thread(THD *thd){# i a+ q F' `% J! n' }7 g
! M3 [( A9 b) _+ ~) q thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;5 v3 J7 F" B! g; g C5 Y
' y% D$ [. r! ^( I5 `+ {6 G) i //线程进了线程调度器" N2 [( o/ z+ B. n) k: ?
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
1 [: k! l5 P8 |$ C5 T1 S2 U: [# q}
$ s8 u! g: j& k( q
- Y2 I, Y; l7 ~5 V; E1 V. e
* Q3 T0 z2 R6 B# E至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。6 @: |3 @ ^3 L( C
7 v0 J9 }' F3 d6 ^$ _4 ~
E( N! r: V: l: {2. 理解mysql是如何处理sql请求 m- @) t9 w: ?& n E" s$ I( D. v
这里我以Insert操作为例稍微解剖下处理流程:
9 q# h8 W# x t0 j
5 T( ]; b! N8 \当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。# ~/ c! F0 R. v3 T& }8 s1 U
8 i+ a3 ^' G" G: a3 ?
; `$ Q( o0 k* {0 J& G4 xstatic scheduler_functions one_thread_per_connection_scheduler_functions=. S# z7 A6 G) y" o& J
{ p3 _! U$ e- u! ]) {
0, // max_threads$ k1 V- X5 D h4 h ?7 }& P" S
NULL, // init- Q% u: @* T/ j: J6 ]
init_new_connection_handler_thread, // init_new_connection_thread
N" w3 c) l5 K$ [8 f9 k4 r% Z create_thread_to_handle_connection, // add_connection
, @1 P; o8 X! @) `& w, K: H NULL, // thd_wait_begin
/ y- G' S* w' Q. D* [# G NULL, // thd_wait_end; x9 d3 |& F) W/ x% N& b$ t
NULL, // post_kill_notification
5 q, V8 p! t7 c, @+ L- A one_thread_per_connection_end, // end_thread0 V. ^2 R0 p! m ^" n- `8 ?7 J
NULL, // end
2 Y$ t8 A/ r. X6 O1 j8 W) k7 O& _) f) ?};7 L J2 j1 T; q7 d, J
* Y) P, S; C& T3 U2 u0 v: m+ ^" ]3 a$ _% j
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。9 u' i' v; G9 o {1 g# D. d
9 q9 q" f; }( U& P' w<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
, s! ]5 j' D& ]0 g, K3 ]# G
2 k: d7 G k: F: U0 Wvoid create_thread_to_handle_connection(THD *thd)& s9 x; p6 K3 h4 {+ T, ]; r" Q& [9 r
{/ c. [; Z* t" G3 f2 D
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
; e$ L& c" ]! P8 T f. F* a0 V handle_one_connection,(void*) thd))){}
& o# s) m. i2 r1 g1 O}
& S( Y: x* n# {8 ~& l* }//触发回调函数 handle_one_connection6 w, u1 q, P/ ^. n
pthread_handler_t handle_one_connection(void *arg)& [* l- V2 s ~" t4 @
{
1 X0 ^4 z/ e4 I5 _; N0 W8 l: q do_handle_one_connection(thd);7 Y' P$ V* b! e+ w0 D
}$ }+ L: @* w [
//继续处理/ w/ z5 g6 U+ ~( }+ T% u! o4 X
void do_handle_one_connection(THD *thd_arg){
. u! z2 f' A2 R' F while (thd_is_connection_alive(thd))
8 O! |9 I" c) w* I {5 {9 y; e9 q8 H h* `
mysql_audit_release(thd);+ k9 d% C) F4 D# ]; s T
if (do_command(thd)) break; //这里的 do_command 继续处理+ q$ [% Y7 G$ a8 k7 H. j. c
}7 S, y: R# q5 Z- d P1 B1 Q9 a
}5 K) t9 l7 S" t
//继续分发
* G+ o5 b- s) ^/ Q' M2 E" ebool do_command(THD *thd)/ A* a, ^/ C" S# m" p
{
M$ l- t5 h5 H return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));" A" y7 N. v8 A) Z6 `2 j
}, `8 }2 l7 }% [8 i7 Q/ A$ t* [( F
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
4 ]3 k1 I0 C5 F5 a{
& d4 D+ K8 V( l# E( P# R) A switch (command) {
# B: a) q" V" ~# ~3 V+ j case COM_INIT_DB: .... break;, a, s5 k: s9 l2 a
...
4 O D1 m; w/ J, b7 X case COM_QUERY: //查询语句: insert xxxx
8 q# v( I% l: g o0 F. ?6 ] mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析& ^/ }6 F7 U3 A: t7 ~: t
break;- I3 [3 s2 _( ?4 _# g
}
& _4 I0 }+ ]: Z+ P0 K- z4 k}
. S1 N% f7 L$ [8 ]$ u4 a. ]//sql解析模块
( F. F/ y6 |% v" w5 B; q- U! Vvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
8 K; M# l, p4 ?6 Z{
8 ^: ^$ G5 H; X5 ^8 M% r0 d error= mysql_execute_command(thd);$ e h1 s! r5 ]5 W8 W! q& e" o
}
- x6 t& N+ `7 O" H' V+ s: P/ \5 {6 W" ?7 ]0 p0 z. _' k
$ ~+ p9 f9 w6 ^$ V$ N<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。, L' r' Y( \, K! Q5 |' l& K0 m( X s
* C3 H( }2 L& Q! S$ p q; P( ^1 z//继续执行
6 K0 [& j. f( K7 h6 C& mint mysql_execute_command(THD *thd)
! ?: h4 Q9 Y' f& ^% q0 K4 t, `{, P8 k/ T& z, _) Z
switch (lex->sql_command)
/ V& B/ k9 k( b {$ w/ _% L* y6 H4 q4 `5 k1 b7 e5 I+ U6 q
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;! j$ }9 @3 k9 ^" v& T! {! s+ H
& X5 d+ c" t7 V //这个 insert 就是我要追的0 a6 l @ d4 S! d. ~' h4 A0 R) I- G
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
/ a4 i. j, n) { lex->update_list, lex->value_list,' d# G% t2 y* U5 t, C
lex->duplicates, lex->ignore);# `/ z2 c2 D8 t( w; e
}
6 }0 I @$ Z- A( Q1 w8 _% D U4 o}6 \- |+ E: w+ u- j5 z# x
//insert插入操作处理, D1 f0 [) E- E/ @6 b8 I
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
" L+ \& F+ P4 @7 [ List<Item> &update_fields, List<Item> &update_values, + E/ k" x4 @( h7 L
enum_duplicates duplic, bool ignore)
0 O/ p5 p4 D" z" w4 \- A0 N1 v{ ^; i0 I% r/ p, o" l$ d9 M
while ((values= its++))
! K" o. p6 N) W; ^ {
% g/ t4 F4 d, J7 o3 m( X; z error= write_record(thd, table, &info, &update);
- ^. v5 {* \5 C) I: W( N }
# ^0 q2 W0 s, ^ U( Y$ w5 Q}' ] V& S9 ^: y% |/ _
//写入记录$ @" ^# ^- m7 x* E( D5 j+ G( r
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)( F( k' h% B( O1 P# c) r
{
( `! ~' k& m i0 z( q if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
8 m. l( `4 V- ?% M, m {
- _& |2 o4 [4 \; C) q2 ? // ha_write_row 重点是这个函数7 p$ f5 L! [7 [7 g2 i) ~5 _/ S4 S
while ((error=table->file->ha_write_row(table->record[0])))
/ W6 w! i* p. t3 f4 z {
7 m: d9 \) u g4 f Z ....
6 {1 h1 p0 n2 v7 f }- g2 W6 p6 b$ ^* N
}
3 D4 W0 ?: T/ F5 J* Z3 w% ^}+ h; g1 K3 t; r2 J
2 H }& v8 Z; L& M6 k. P |1 i9 P! V
6 H z$ J; ]! P: }% l
" ^! i3 a3 J# y& e; K: e& [+ |可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。+ S* {; _( y$ [$ z# u5 S, A# y
& \* P S7 l0 S j6 [
<3> 继续挖 ha_write_row
" [( e( e, ~9 U# j( r4 s# M! q5 k. _5 [% m
int handler::ha_write_row(uchar *buf)
6 H! }1 u" z& l0 c( e{
. @& }8 `. ]! W# ? P1 A a MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
$ ~5 G5 @, k4 W0 _, B/ j}
{8 |/ C8 ~& T, T
( H4 e2 _3 Y+ Q! P. W) I//这是一个虚方法7 _* i5 o+ t/ u
virtual int write_row(uchar *buf __attribute__((unused)))
9 t( O% G6 M" S{
- t% B8 N- j2 V. A) I" t return HA_ERR_WRONG_COMMAND;+ w; K+ V/ z( W4 d% ]7 [; J' D
}
8 Z5 t9 R- {/ ?0 {5 K3 h. V: j; J0 \* i" M$ o6 J
+ r. I _' g& r1 v& K
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁& O4 [$ ^: a% Z9 X
' k$ V P# Y Y1 X( c9 A3. 调用链图
7 }; w. z4 V/ }3 [2 m |这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
# ^# | T7 |8 y, M# b( C![]()
) j" r8 H! \( w# |9 q! ]4 a3 ]' \& n
; J7 S- ]7 T9 _9 k @8 u三:总结
4 f. \* b. D: S: f. J0 W大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。6 T q7 Q$ m0 U2 Q- X! _& p
————————————————, Z9 ~# r- M1 [, U2 X6 x- X
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
% T2 r& K, A3 s, R( W( G原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
* t: _4 |1 _, ^! {# k0 F" ^ |
zan
|