- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55571 点
- 威望
- 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 田老师国赛冲刺课 |
一:背景+ b, M% V0 z6 P
1. 讲故事
& g$ A- H+ I& n' {- S8 k- ?( D最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。 ~) l8 d) B o
/ {6 Q g7 v* _$ ~6 J- i
二:了解架构图
. W- e4 z, l% T0 v' o" zmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# B9 R' `: r: w7 a
; \+ L8 e: s, _( h$ P. J1. 从架构图入手* G0 L. q7 k3 z# L
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
8 j6 {2 c% L$ {) p" D/ a![]()
4 S" C! \% R+ ?7 f1 f. f2 R) ?' r. G. I0 \# g+ L! Z! M# ?3 F4 S
" h" ?4 g9 R! o$ h. d( r" a l* E其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
; \9 E* B9 L+ B2 {4 W$ v" u0 I2 r
4 v" O7 n) F: B" n3 P2. 功能点介绍
7 F; J* L N& B. }MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
' A) B8 \3 m/ |: o" k' M F
8 T0 e% E' C8 v1 }$ p" v+ y<1> Client# l" b* a ^/ y! Z; Q3 L, Z7 Q
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
7 F: T9 Z2 t8 c# d4 y' X/ g- D. A( ~# O9 p) P
<2> Connection/Thread Pool
& V* r Z. F/ V$ G5 K, iMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。; j T+ ]: s! a( F: x8 l+ M
" e8 W. D2 }$ @2 b/ p: W& u; \" o
<3> SqlInterface,Parse,Optimizer,Cache! @$ {* r% X/ i @4 m/ N- y, E
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
' r4 \5 J) }, ]" l5 F) H8 p" K0 Z$ s( S; x% u/ Q3 k: [$ @
<4> Storage Engines5 M, r3 I ?* ~' ~
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
* h# e9 N( o) I. E* \
% R" M* V: `( V三: 源码分析: x2 C2 I$ \( H a: z! X
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
! `' K7 F$ \9 f" t; {; w6 {& t h% |
1. 了解mysql是如何启动监听的
/ R5 \0 K8 _( f2 L# {0 J% c手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。5 C- Z- F4 |5 d5 G" O; W( }5 w% Q
![]()
$ k! g! P- T& B0 X
: w; t$ Y" B6 H+ g1 H# {, }' e! c( u
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。/ e; ]6 l) r' t0 [7 q( x# T7 U8 {
: `" R' y: K* ^0 Y; m# g3 `/ {<1> mysqld_main 入口函数 => sql/main.cc) B- R# g" S! h5 {6 U6 ]! S/ A$ W
- x9 J1 A% s# `/ I) }
6 Y, h4 ^) W+ T0 X
extern int mysqld_main(int argc, char **argv);$ }/ U/ g1 l0 _; l8 K) g( w. @7 H
& O0 |1 C' b5 w$ D) C
int main(int argc, char **argv)' I3 G6 u' P" T& j$ h
{- U, }4 l& _# a: T; V& L0 U
return mysqld_main(argc, argv);% D: I7 w/ L* L% ^: B
}
8 c* V9 S: N5 @1 [" e* w, _* k( B8 x4 Y
T0 b$ ~7 M1 w6 D; v
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。% t) c, k5 s" C
/ C& x- Y9 ]2 g! Q<2> 创建监听
& |$ J) g- m4 H/ @ y- l# [. M
j: z/ u2 j0 B% F- X1 o1 o
, {1 Q' k( r* i. m Cint mysqld_main(int argc, char **argv) f" [6 ?3 Y; c3 u
{
$ P' {. B- i+ B, \6 g- j6 C //创建服务监听线程, M* C4 y' q! h, u
handle_connections_sockets();) o+ [( q/ d, d% F( H }, U
}% R3 H9 C4 m R! W Z
1 I3 s2 g7 z$ | b2 e
void handle_connections_sockets()
# G! M; v2 }8 y) a; T/ [6 R{
& o, o7 ]& b: w- Y9 `" A- ` //监听连接3 |. c4 c8 R( s$ Q5 b" _+ ~1 v) j
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
% |3 Y$ h/ N6 |+ p% a" j (struct sockaddr *)(&cAddr), &length);. |* k" h( ^ L, R& O
, _$ X A- Q9 S7 c- n+ ~
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
' s/ i+ E8 A# v8 P Q: ] thd->security_ctx->set_host((char*) my_localhost);* ]- A0 J# V) m
. q5 A5 k' k9 y& ^* e //创建连接: _. G6 u: `2 I
create_new_thread(thd);$ F! w6 B8 Y7 |! d- S
}2 S* F- ]' T9 s
9 n' m" }7 n% p5 O' b
//创建新线程处理处理用户连接3 w. p2 e' f0 E1 B/ s' D* h! f
static void create_new_thread(THD *thd){- U7 U7 e$ e$ j2 C* z
i7 [) z# x4 a
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
8 [1 D# o$ S9 h* @
$ g5 K5 g; T9 v //线程进了线程调度器
/ ?! m' O0 Z/ b8 M S/ W MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
{+ t' _- N$ Z, W}9 N! L% {6 r) `, Q
% P# [- `& i4 n$ E# w2 N1 S/ P
; I0 Z. W( T, U% \8 n3 @至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。9 b+ m. i3 B1 z {
; |( Y; _, _. k% V- Q
6 s! b, O2 k" k7 Q6 o8 R7 R( ^
2. 理解mysql是如何处理sql请求
) j( ^7 _ \/ M2 D$ y% {2 m这里我以Insert操作为例稍微解剖下处理流程: z9 C% Y" e* B/ k; V9 b5 Z
" q- Q. s0 i+ R! J+ @0 o- a当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。; H% r4 w9 L( C3 v' S2 M1 i/ N
6 |5 ` ~! d! u1 [1 H5 s
7 B& `6 w0 c2 ~" f6 g1 H2 I! l' Zstatic scheduler_functions one_thread_per_connection_scheduler_functions=
7 p9 q% y$ v; o0 i: h{
1 Z# V1 v# c2 P5 A1 a; e" o 0, // max_threads) Z8 j6 q9 B. \) {4 _
NULL, // init% C. r' u' o- n3 c/ s; W
init_new_connection_handler_thread, // init_new_connection_thread* r0 _. ~2 e1 i$ y
create_thread_to_handle_connection, // add_connection
- C& d4 L; {9 W9 w" X5 k! E% d NULL, // thd_wait_begin
z1 M" l( h) ?) h" W; s9 R NULL, // thd_wait_end
9 h/ E' q. ?* ^; q5 {) | NULL, // post_kill_notification
0 z/ [1 a4 v" R one_thread_per_connection_end, // end_thread
% v+ m0 \1 h, q3 j/ }( j NULL, // end1 Y" A; V [$ u$ u9 r
};
( s8 a6 [* y t1 q! t7 P% U) J' b. @
3 p d5 @ W8 P从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
0 E$ K% V+ p5 q0 a6 `: j. g6 p2 o! C5 I
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
' N" n- p" j1 j: r* q- j5 s* a0 f/ c/ X( p; C
void create_thread_to_handle_connection(THD *thd)
2 G2 r3 C/ i/ g o7 Z2 d( c1 b$ f* n{% t3 U. K* T1 ?; R$ r8 m) F0 O
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib," d- p5 a+ A+ P
handle_one_connection,(void*) thd))){}
" _* _5 t9 G& `" c0 S7 {1 T}
8 R+ G2 X: S% n) [+ i# ~//触发回调函数 handle_one_connection
}+ b- I; q/ p4 J& Kpthread_handler_t handle_one_connection(void *arg)9 }. K4 C# t0 H1 t8 D
{9 e6 `. O% M0 ]7 y, P1 X: M- E7 V
do_handle_one_connection(thd);
9 o J: s @9 x; V) G" C}0 z" y! Y7 Z4 s, e
//继续处理$ X H& A; [+ l3 M/ W& u& m
void do_handle_one_connection(THD *thd_arg){
' v4 o; {" g/ V& h* | while (thd_is_connection_alive(thd))8 L t5 d. `8 d2 q6 C5 T
{
. K! x) P" \! h2 W mysql_audit_release(thd); m$ z8 C3 G6 C% w6 k. z+ |1 v
if (do_command(thd)) break; //这里的 do_command 继续处理* I# D; [' K3 s7 k8 _
}
( d3 }% L ^8 o1 _}
l2 N; F/ X: G w! d$ A//继续分发
" b' z/ d* y) C3 V1 d4 `* z" ]bool do_command(THD *thd)
- ^( S8 f6 y C7 C{
( b# @0 z; }6 V& j! x return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));4 f5 s" r' [6 A/ P9 u- i
}
* _/ M; Y" U9 Z& F* jbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)( J1 Y8 W& ~ j/ `2 T. ?
{0 o' Q h4 s9 m/ @' L' {. k
switch (command) {
0 `7 S8 u8 h: G" B5 r6 u' w2 h case COM_INIT_DB: .... break;
3 e: O' N: a" Q9 _6 p2 f0 v ...
# y0 c) [. ?9 G case COM_QUERY: //查询语句: insert xxxx
" e3 C$ Q3 m5 E- q2 F6 n mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
; o* d- h9 J# d% @+ T% P2 V' m break;
" c+ `" w- R8 A1 b. M9 D& P, i }: v* z- ?' W# J# d: x- q* p$ q
}0 V- x+ x- @2 L
//sql解析模块1 r4 ^; P. s7 M5 V$ @1 t. l! s# l
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)5 a4 D% T' S2 M E5 y
{
* U- r4 r0 r3 q/ X9 T error= mysql_execute_command(thd);1 Z% _ i4 s' L; r8 ?
} b( v$ ^5 o$ B0 r( e
2 u- p5 w6 H3 z. n
7 ?# q% U/ _5 I8 i: f
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。* O+ Z7 O7 {- a9 C
Q0 ?& _: f# a0 F//继续执行- a3 \/ c* I/ f- {8 |" [8 v' `
int mysql_execute_command(THD *thd)
! L2 `/ X5 W/ d5 J: R{) E/ V4 S( r- ^5 _9 }
switch (lex->sql_command)
. G" j# K3 e$ P {# A, y# T# b8 T8 E3 N9 J- ]
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
; k$ E# g$ r6 D* O& }% p% B0 w: r4 a2 `6 X5 J
//这个 insert 就是我要追的# }/ j2 D# }9 W. X
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,$ Q$ k3 g$ K0 t
lex->update_list, lex->value_list,
$ f% W; H7 U3 t# B, f, k6 c lex->duplicates, lex->ignore);
8 W/ B' @& |( R- c$ E. c }
) F9 a$ C% [$ Q5 X$ |}
7 T; {6 f( i9 `" E//insert插入操作处理) c X# V: D0 F9 i, \ r0 I
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,1 R+ f& q! N0 A3 v- O
List<Item> &update_fields, List<Item> &update_values,
8 c" c. A7 a0 e& P/ T enum_duplicates duplic, bool ignore)
2 I$ u* a$ U: x% r# A; e+ j8 p3 M: i{
8 q. G6 e0 }0 E while ((values= its++))
4 j2 V$ K3 q5 S" X {
7 d' H# P! P7 t- A6 ` error= write_record(thd, table, &info, &update);
) u% t/ F% g1 a# O. @" p' H }
. o B! w4 }# W5 I+ L- z}
% |9 j1 ^6 }- o9 [3 K//写入记录& R% q9 t; T. _
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)) ^" O9 B: M6 R0 E
{6 S" `* ^' [ B7 ^
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
; t/ J' D7 I6 d2 r: _( ]7 N {
0 E* x8 \1 F% | // ha_write_row 重点是这个函数; X' \+ O4 R% ~+ `+ s8 Y% g/ J
while ((error=table->file->ha_write_row(table->record[0])))
2 P. t8 d; [8 T; J1 k {
" N2 C- {3 k3 G' ~ ....
# K) `& p6 V9 ] }
4 P0 m# b: G S' ^0 ?- q }4 B+ Z% N7 M! ?
}. B' ?* I( r$ Z# `4 J" O
# m- B \4 }5 _: _
) t; r4 u& L. l4 c3 i9 a& M% J# X, X- Z$ c% `, z# Y5 R
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
9 f3 u. ~, @1 v$ ]9 u) T8 n4 z4 t/ \
/ G& z2 V7 U' Y/ P4 D<3> 继续挖 ha_write_row" @: S, N& w1 M+ @; m1 m( Z) {0 R) f
$ L: Q/ m }! K c
int handler::ha_write_row(uchar *buf); ^6 H0 \( Z) r, z# O
{ g! I/ d* o/ V2 N6 s+ c/ r
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
; ]1 e" ?, O# w0 \! Z. x}& `8 Z- N- Q; ~2 T7 |
1 C1 F% e/ ?) }5 S# i" }1 k' q//这是一个虚方法
6 G5 T) c0 j+ c7 L+ N. K! g& ~' gvirtual int write_row(uchar *buf __attribute__((unused)))0 f9 D0 p5 o9 x. o: Z# w: k; V
{
% j5 g8 J0 H2 f& V6 O. x return HA_ERR_WRONG_COMMAND;- O! q) y* J) ]4 r3 U: f
}! @* o. n$ i. E1 }- ^- l
* r5 Q* X X8 M# _' V0 E# A* ^
2 V6 O, j2 M% u. B* t0 y
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
/ X+ t* l$ j1 K9 |+ H/ Z7 t- ]1 a W$ N3 @
3. 调用链图
6 E! o3 e* ?' I- k' E9 W2 \$ b0 k这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。( c! g$ X2 g2 ~& i8 b5 P
3 }. a6 b& ]0 y
5 e* Z t8 z& J3 R5 _1 [0 B4 {9 i, t& V
三:总结
) |2 T8 r, O% j* h/ C大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。" w+ q2 E7 L( E' S# I! H
————————————————
. D8 T, N7 B3 F" B# p7 G9 _版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。' C# V) y1 X# x& S
原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874156 ?; h4 ^; i/ P! \
|
zan
|