- 在线时间
- 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 田老师国赛冲刺课 |
一:背景5 E0 }) B4 U- J0 M
1. 讲故事
: w5 R4 V5 q9 T Z: E, U最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。" h; u6 {/ f5 A, J
6 z7 r* W8 P5 `' @% o
二:了解架构图
2 D- P4 M/ ~2 s! Wmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。6 G4 V K! ?* b( V0 `9 w: C8 X
$ g* \; U% Y4 I. B& O7 A9 k/ H
1. 从架构图入手
) u5 z2 A* @( U/ U5 W大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
3 C! B, u& K U ; B" V" z1 h* n5 T" J$ o( c- M
( X$ N2 S4 ^: n4 U& A& `
. i4 e" b; n$ C4 Q. M' d1 X其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
8 B8 I4 A' i/ Y7 G& l- Q# w* U
. _1 b' q. x5 u2. 功能点介绍
0 [9 T3 Z' A% W/ xMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
8 @( S _5 B) n3 _: u) r# c% ~$ ^0 D, [) g, z
<1> Client# N+ y0 q7 z( S/ G
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。- D0 i3 l- K0 j2 z# ^' C3 D+ W, }- t
; C+ h" B; X6 }% W" Y- Z m
<2> Connection/Thread Pool
2 ]8 w9 E; v0 z7 Q; f9 nMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
' T8 N5 ]+ I, b, J" n
1 h7 Z7 Z5 W2 _; ?<3> SqlInterface,Parse,Optimizer,Cache
1 F* p X( L/ I- x2 k对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
% p( C! s' B% o% \4 \
) E2 P* j: S' e& y! x& d+ |: l<4> Storage Engines
6 t$ i( [' Y+ y6 q$ ]" i+ ]负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
4 c1 S+ q; M3 ^; _1 s1 v7 J, Y( L- {
三: 源码分析. T, j+ [* [4 j1 r0 _( O" S
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
* X5 ?7 `% j; d5 s9 q/ z0 P( L0 w2 [9 c4 B6 T/ `) T# D
1. 了解mysql是如何启动监听的
* `! V) o, j5 F手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
4 |7 B1 t( t& @/ [# r/ _+ J3 W6 e ' n8 N5 I4 }6 o8 `
# O- m9 F: |, `7 P9 l' O2 }. ]# F: H$ I! f$ l9 g2 O4 l6 l& Z
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
2 r9 X' C, t) w4 U: Q1 a- d; h2 Y3 k z" |) e/ P8 ?' w
<1> mysqld_main 入口函数 => sql/main.cc
" h* K/ w6 I7 h% L' Y- _0 A5 L3 i1 u0 [' w( n
( C: O; g. U! Y% G; Y) ~
extern int mysqld_main(int argc, char **argv);
; N( q6 Z% _+ B6 ?4 `* E, t
h% c; ^$ r, O& Hint main(int argc, char **argv)% c6 N8 k; Z1 e$ ~8 j B) \1 E" `
{4 A- \7 ~6 b( s: c& `& T0 l
return mysqld_main(argc, argv);
( v. k' e$ r' E D3 l/ s) Y}
5 r# ?+ z* G2 Y5 g% d/ ?" D9 b0 k; a9 ?/ v2 q* n7 V( M' G' @) S
: c8 \2 g! x3 H* H2 {这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。' i8 w) r7 I6 L6 z! g6 _; D
2 ?' }$ j! B$ M7 y. X+ l1 {0 {* e<2> 创建监听
3 A {3 f, q: X- a
6 h6 x& q2 z, U# V0 K! ?; `
[4 v8 y0 h6 g7 bint mysqld_main(int argc, char **argv)) u: \0 K2 s# N* L/ y* `* {* ]8 @
{
. b: W. A/ k2 }, ~ //创建服务监听线程' c+ E" K2 z$ z7 d3 _! l
handle_connections_sockets();
6 K; P0 T. k, m/ |; ~8 c}
) z9 z3 b$ h. c& {' P, M$ b4 Q4 G6 [0 h& W0 P9 Z7 D
void handle_connections_sockets()- H1 B* n5 d e4 A( [% r
{
2 [" V' Y! `! P //监听连接5 T) M* f( z0 C$ d- ~" X
new_sock= mysql_socket_accept(key_socket_client_connection, sock,4 k# O8 z6 p4 ?
(struct sockaddr *)(&cAddr), &length); n. |4 R8 G% b0 T" X' ]5 z
4 a! I* }; @+ u" D/ Z1 l1 G
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
, |" |- F! K( s thd->security_ctx->set_host((char*) my_localhost);; M8 |( v; N: _ `3 `* F. `
; g: B, K7 t* B, q7 A" p
//创建连接0 a2 i" q9 @; l4 D# I
create_new_thread(thd);
1 C6 X( x* s% V7 `}
8 n; e, Y# v0 y' x6 n) D1 U
; H9 z8 A% F m1 R. _- F. F/ K//创建新线程处理处理用户连接
" m! M4 N7 M! Wstatic void create_new_thread(THD *thd){" m# `3 @! y. i, T8 V+ |4 w: M2 ]
6 m! G9 c0 H/ n/ t9 h: a7 f thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
$ j/ W, r4 g V# f1 S# B* g$ C+ L8 w! J8 x/ @9 h, f3 w7 W
//线程进了线程调度器& o7 l* t0 Z: |4 s( C
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); }, S4 F6 {7 b( @" D6 Q5 S
}
4 D" T; R3 O% A+ a/ B# D' u3 J6 g* J
: s1 g3 [9 n# b- W6 G
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。1 C" W; T+ h1 Q1 ?' r0 s
& c: q. k" H0 T" t/ G
; @5 J3 i* H2 z) V U! W0 c2. 理解mysql是如何处理sql请求
8 U+ G, M/ c- a. E& k+ Q0 W这里我以Insert操作为例稍微解剖下处理流程:. z" k- I3 q1 w! u" F0 y4 i* `
7 e5 j. q5 b& D# o! y K- A当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
B- W, X# v- T" r7 i2 i6 |4 b
u4 d( a0 C9 K; z
, A* ?( k9 O" T& l ~1 n1 H+ hstatic scheduler_functions one_thread_per_connection_scheduler_functions=1 o2 R4 k: n+ _9 _) {/ i
{
: f7 R; u+ Z$ M4 T1 g 0, // max_threads, _# Q" J: F+ E0 T+ x v
NULL, // init" Z' H- P: ]! z u5 F
init_new_connection_handler_thread, // init_new_connection_thread
: f7 V. W; I, y* h9 J create_thread_to_handle_connection, // add_connection
9 U" h% F) ?9 ~, [9 \3 Y7 a NULL, // thd_wait_begin9 @+ S( [6 N% k2 z* k- H2 {
NULL, // thd_wait_end
1 C1 S5 m$ h6 Z6 E- R NULL, // post_kill_notification
1 S- u8 A! J" }( S1 i2 M; y5 C# r one_thread_per_connection_end, // end_thread
8 q9 n1 @& U ] NULL, // end) \# u5 }! b5 U" c" j" X7 h& F
};7 P9 J, T) k. C7 w9 X. @
( }% I4 G B6 @7 T! g c! D/ n
% L9 ~, x' |% |从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
0 ^. A# g0 K+ P% }) g9 m! D# V0 k) F* x/ y: ?% |4 A
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪6 f/ {5 R0 P' P6 t! B+ ], [
+ d( e, ?1 i M, t& p) b
void create_thread_to_handle_connection(THD *thd)
: \6 I# p. q; E7 C$ ~2 Z{0 I/ n& \$ f& q! b" t
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
) `8 e, F; \- s handle_one_connection,(void*) thd))){}
1 U/ k( a" N) \5 U' x ^5 [}
8 I* E6 M3 l' ~% w//触发回调函数 handle_one_connection% U* o) h: J. S& G5 F i
pthread_handler_t handle_one_connection(void *arg)
+ V; \9 i. e+ N' h- L0 k% X+ y' v% h{
+ G" m* I$ S0 c4 \" g0 a) E do_handle_one_connection(thd);
/ O) [5 h! p; O: o9 ^* F$ u}
/ ?. U: d; ?) x0 m o" H//继续处理! u$ B& q" ? }( B
void do_handle_one_connection(THD *thd_arg){
+ v* ~1 ?& K9 }# a% O. Q while (thd_is_connection_alive(thd))
) D; o( R2 Q# @8 v7 l' t2 c {4 u# E1 N" ]/ P' g
mysql_audit_release(thd);- `1 I& d" X+ ^& B, K
if (do_command(thd)) break; //这里的 do_command 继续处理! g+ q* }! E8 q0 d# R6 S9 r
}
( B: _# j! {6 T}
% X1 n% B) t3 T( f# {//继续分发
' ?( K/ v) | f6 O/ p# `# Rbool do_command(THD *thd)0 s, v4 U5 J" u; c. z ~
{, h9 I5 o6 h6 u1 T# y7 |# L3 r
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
2 s1 Z5 `" t) Q0 J. |6 E7 P( x}
. w; o2 T& U& o+ ?4 x7 ]* m! a, Gbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length). ]2 y1 y- v2 D& C3 E0 @! o7 |' {
{
# q+ r$ m' Q D. V switch (command) {( a0 n9 q4 z2 P: q1 D
case COM_INIT_DB: .... break; l3 L1 z- o# R
...
3 A R% i* |2 c# A! x case COM_QUERY: //查询语句: insert xxxx
" h' n+ G7 M5 k0 U J9 R mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析2 k1 p$ P- a( T' u
break;
1 F3 G4 j$ R4 V3 G T) E8 r }1 L, p8 m' O+ Z& _, W
}# I3 Y: O1 |8 z5 t: t
//sql解析模块# Z+ L. Q7 F3 p, n2 \) y
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)+ Z0 l5 S. ]) g2 e5 U
{$ @% {& x+ E* j1 m7 ]
error= mysql_execute_command(thd);
1 | o( B9 w8 y# M3 @}
+ U' n0 |5 O' w: v/ X( o' y; t! M" M
8 e! e3 T! W l+ K7 K& P0 q5 P
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
* x' a' L$ I+ ~5 f9 u! r) G8 S8 p) I% a3 }1 V, i
//继续执行% c: x1 [+ x2 ^; H3 E. z' |
int mysql_execute_command(THD *thd)
" R: i" i1 B$ L+ H8 |3 y{
" t9 V- u; e) ` switch (lex->sql_command) & d0 v* Q( _. {
{; V1 R9 U5 ~$ o; x/ I5 w) N$ X
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;6 D; S! y, a# E+ J
3 L6 V7 k/ F0 Z% `+ y
//这个 insert 就是我要追的
5 R5 z7 J3 S) [' K* m0 N) B! X case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,2 H( C- {4 x. V4 }" L
lex->update_list, lex->value_list,7 W/ o9 ~/ J8 k7 q
lex->duplicates, lex->ignore);
9 A! y( ^9 ^8 T8 Y* x4 s( J }' W* F1 o) ]. ^3 X- k
}/ x& {% Z. }! w- {7 A! v# C: K: X
//insert插入操作处理( w7 l: a& P% H1 q
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,' P! B& d8 k1 a! e6 E5 D( V
List<Item> &update_fields, List<Item> &update_values, 8 h$ X1 |& y, Q/ w6 \
enum_duplicates duplic, bool ignore)) M0 R6 \! Q6 M/ }
{
: w4 _+ b" P# T+ a# F# x while ((values= its++))/ }6 v" W5 a6 w- y n
{
8 x, g+ V6 Z: s* | error= write_record(thd, table, &info, &update);
; ?9 s6 C3 {; a+ a6 X4 G. L }/ M, b% G# l' h8 g' W
}' c! y0 G, Q3 m; X! m# Y8 f4 h
//写入记录
0 f8 K! l: t# `! ]3 d1 _int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)( X; ^' d9 m) h" f- i$ X
{
3 X0 A# _3 Q- J A; f if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)9 h% Z. s* V4 S0 K; F" U/ r+ c( L
{6 _/ m. @( F) ~; V; u+ {
// ha_write_row 重点是这个函数
5 c: d/ W7 V3 H& l0 f: ~! P while ((error=table->file->ha_write_row(table->record[0])))( O' y: U' p4 I
{' X. T! q) ]4 C* E' x& F8 o
....5 |: t& _+ j" w8 t3 q) l: o9 _
}
# c5 E% ?& E4 i5 k: e }& _' {3 N6 N1 b- b) t9 p Q
}
8 c3 _* @$ E g4 s0 q% X( T. ~
4 e8 c" W; U4 F- q0 Z3 h
0 P( z% x. C$ a8 o( q0 w1 g2 x% @) {" [( ^
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。5 |1 J- x \9 p- V5 j
9 S5 Z5 v$ T6 U- w<3> 继续挖 ha_write_row
9 f0 G9 ~1 h1 I5 D
9 `) v7 Z( q( h3 T: E" |int handler::ha_write_row(uchar *buf)
0 m" r: T1 D n: j. X, b/ U8 Z* s* V{
6 |9 F u1 w$ I/ L1 ] MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
2 L' Y p0 u& d7 y- n g y}
! {% B+ f |, G# O/ e% H' A4 r( c f" `1 D8 _5 j! o% j
//这是一个虚方法
6 k' R& f5 L0 {+ n5 Tvirtual int write_row(uchar *buf __attribute__((unused)))+ R0 I- G9 z ]4 Z" i
{) |7 U$ i$ W J& y: y9 ^ y
return HA_ERR_WRONG_COMMAND;- _* E' m# ~' Z& r0 |
}* E. k6 E: @: y. o
3 f6 K \( Q9 [4 s) K: X- O
7 |% E/ b2 C3 w; z& b* \3 v
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
' O* u' o2 ^! p# H2 I3 x! O% o% a9 P& v" k6 H$ Z1 g$ W9 I! ^0 z
3. 调用链图- U) M* R$ N8 x: ^: [
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
8 V' e9 X8 B$ U# }5 D![]()
L# g' B5 ~- p& i+ v9 w
3 `. j% d$ N$ y4 Y- X- B
1 a7 t8 y% Z8 e; A9 X, H三:总结& B- P8 m& n% T
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
- n) w3 W0 e9 K1 D————————————————4 _, n' P& q/ ~& F) P% q
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
) }8 y4 n5 y0 z g原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
9 T$ A# I9 b2 I$ S7 G |
zan
|