- 在线时间
- 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 田老师国赛冲刺课 |
一:背景
9 s+ q4 E* m3 V' p1. 讲故事
. y9 [7 p' E# j8 N" u7 F最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。2 w Z7 f# D# ]* X9 n% i% N5 d
: m/ d! A: p# L
二:了解架构图$ Z8 T1 j( r4 F, ]
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
# ]: I {# L6 ^5 I
2 s9 n9 u0 \* X! v' p% P1 @1. 从架构图入手( R( D- O3 d# Y' o4 C4 e3 `
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。2 D( _6 |6 K* @" d
![]()
% D# N. \+ T+ j# l5 _ C* [, B$ I7 ~& R W; W+ u* w
1 A( h3 y- R; y: y/ k, H
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
9 r" }" T! C$ A' l- J# I! ^& U0 j# I, Z. L
2. 功能点介绍
5 w3 I- ~& c$ G' l. i1 t8 t# RMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
: m& O' W4 G4 I0 C8 @1 e) s) B0 j, n& F4 ^
<1> Client7 X6 ^' c5 r; r
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。7 G. O o; a! F, ]
# Q+ r1 N+ ?8 s2 S
<2> Connection/Thread Pool6 ?/ Z; b3 \% l Q6 \7 {# ~$ g
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
0 j2 ^. b, ]" J a, M; l( X1 \
* W- y/ @/ ?4 m) B' M* |4 w<3> SqlInterface,Parse,Optimizer,Cache- {1 F7 e6 r# p4 v. h& c- l
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
3 [9 a( I9 u& x- o& F: c% L& e# m, j6 d0 [( p( q
<4> Storage Engines ^$ C$ w$ m8 o4 @8 v! g& W
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
) E& X2 [: e3 R3 ?: j
' w C# }7 L' V3 @: s6 y5 \- i% N三: 源码分析- {1 W" S" X% X* c5 ^; \
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。! a1 w, y' E( w0 \0 K- Q0 V( w" h
; k# C: [: X3 u3 i. e, p/ _& i1. 了解mysql是如何启动监听的/ V5 i4 K/ M* Q4 u) c, v5 X% C
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
! ?( J# ~) h7 K5 y: p5 ] * C2 @) d0 R! _# ~# v1 D T" I
( k# p/ k1 N4 |) n- q6 B2 w: c, G `
- K) F9 @1 ~# z2 c从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
: k7 O# `! ?2 \
+ I* S5 T7 x8 [<1> mysqld_main 入口函数 => sql/main.cc
. s' W7 c( K% q
7 ] m5 l+ n" L- } K% {& i; B& Q1 C$ Y
extern int mysqld_main(int argc, char **argv);
# s8 E, G$ ?" u3 |/ ~2 G0 f& o u
- O4 v! i$ N* w5 Q$ w' d; sint main(int argc, char **argv)
" |# l6 f6 @9 e9 K+ b' |{
, L# g5 z; z7 V4 P+ m/ K9 C# E return mysqld_main(argc, argv);" L/ D; O# C, U; M4 e9 s; O
}
O# D$ Q! I* v0 `2 E
! O( x" f3 l$ v8 A% ?# H- j {' N3 h: U6 p8 Y6 X' d9 n
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。) l/ y/ ?+ G8 l4 e8 O2 x
% V6 p/ Q9 N/ U3 V( v7 j$ H# e; z<2> 创建监听1 E) D/ `! D- \1 L
- Q+ s1 x) I" D# N' |0 T. m( c
4 B7 s; M0 O) k2 o/ V# W# k
int mysqld_main(int argc, char **argv)2 o. F3 x k6 @* h/ x- T; {
{
; z& ~0 Y3 U* ~$ A! U0 A //创建服务监听线程; N: d# F3 f2 ~9 T0 I3 {0 T5 M5 p& r
handle_connections_sockets();
4 a }: S8 K8 e6 i+ F: }}& i. k4 S. J, V2 ]
' r3 w. l' j! G$ _9 [4 |& j' Uvoid handle_connections_sockets()' g6 @% a8 Q- v7 ~. ~8 c8 L
{) K2 f3 U1 n! u# X9 i+ S
//监听连接" y& u. d4 B& n- \- k/ l2 _" M& T6 c
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
* Q+ P9 j8 Y9 M1 F (struct sockaddr *)(&cAddr), &length);
/ I& ^) u1 ^6 p% `# X4 B, ]6 l4 v
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)): `7 C5 h: \4 Y7 R
thd->security_ctx->set_host((char*) my_localhost);
8 t! Q. |& F ^, d% `" c$ i6 w7 S3 c4 x5 ]
//创建连接; Z6 R. `' h* a1 Q
create_new_thread(thd);
' W. L* c& T$ T6 d8 K}; Y. ~( A: l; ^
5 A! p/ E3 g9 ]! Q5 y* R
//创建新线程处理处理用户连接
5 a- @0 S) ?: U3 d6 i, vstatic void create_new_thread(THD *thd){) F( Z) E& E$ N
1 ~( N+ k& n6 K thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
5 ^1 d& x3 p% C. w
' `6 |$ F: D% |# i: i' k //线程进了线程调度器) ^$ A2 q/ j ^( h( C. t
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
! S" W3 v7 E6 T6 N% H: M S0 Y3 x}
& x: v/ q3 n+ h. X3 v0 X5 s; d, ]6 M5 m1 p
, ^& w; p3 e9 I8 K0 S6 Z5 P至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。6 ]4 G* @' N7 S
" O$ r% S% M1 g3 j: Q
! s6 @5 h5 q+ h
2. 理解mysql是如何处理sql请求
3 J! Z! q- k- n; o+ X: \这里我以Insert操作为例稍微解剖下处理流程:! o" Q \5 ~' ?; z' y% D
. L' @+ x( p# o! R: K当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。5 h7 d! Q& G0 n: _
4 j" d2 K7 }! b, S
# Y8 P: U- R. o; J
static scheduler_functions one_thread_per_connection_scheduler_functions=
# e1 w( f+ P p f* J' Y{
5 k, H! X3 O( u5 ^+ ? W6 h8 g0 S 0, // max_threads
+ z V, U0 F' ^: T NULL, // init
2 p) O d$ p1 D6 I+ k0 i init_new_connection_handler_thread, // init_new_connection_thread
0 [7 f6 I- \' H' u create_thread_to_handle_connection, // add_connection
. _+ s8 s2 r* A* r2 a NULL, // thd_wait_begin% H1 ?3 e3 x) D$ z9 p
NULL, // thd_wait_end$ W" c! _7 d |0 g, r N l* } X
NULL, // post_kill_notification
8 k$ g& K0 B" i; ` one_thread_per_connection_end, // end_thread
% s) ]- k6 ^' K1 w+ `# G NULL, // end
) w, x5 y$ z! c ~};$ B/ m7 [7 @- H, E4 g& {$ K. _
' k9 x! h; G9 D3 _! d; J8 J
1 p4 r& E5 H5 G& {: s从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
% |0 t& `! r {& u% Z& d* T# S0 k' L* B4 X
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
6 Y9 e# q9 p) W7 ?% g7 u6 R0 _) `( K% X+ h5 K
void create_thread_to_handle_connection(THD *thd)3 q/ n6 G' Z0 X7 g) {
{ `: [; P3 y4 A) d6 u! o; l
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,8 ]' Q0 |& `$ W9 s
handle_one_connection,(void*) thd))){}2 B! @2 Q# r* _9 W+ C3 F/ s
}
" P/ l5 \" w, J. b* b//触发回调函数 handle_one_connection
@9 K! w8 m$ gpthread_handler_t handle_one_connection(void *arg)0 q T7 U8 x+ [4 \
{0 L2 ^+ B2 y2 H5 h; s) n* l
do_handle_one_connection(thd);3 T& x/ }+ w- C1 H: k2 i7 t
}* ^- \# h9 B3 V2 w& I9 p
//继续处理
+ X( f1 H* }. D* uvoid do_handle_one_connection(THD *thd_arg){
, [0 O7 M9 T U4 U* P; T while (thd_is_connection_alive(thd))
! V5 U1 A8 g- W) A J) ^% h {' h. [1 ]+ Z$ V! ~; A
mysql_audit_release(thd);% E2 Q$ p- ]; e1 O1 y$ p6 e
if (do_command(thd)) break; //这里的 do_command 继续处理
) c: t9 Y6 K3 D( ?9 l* ^$ }6 q }
- d4 \/ }2 o3 P3 J}2 [( {- Z1 r9 u: B; a9 U. o
//继续分发$ n+ D V) [: `0 Q
bool do_command(THD *thd)
$ f, |8 c- `' W; L4 \2 {7 U{) t0 ?, |# r z6 A
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));" ]4 H, C: R; O V& S( W
}" C8 U5 `2 [2 G
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
! D( _6 F& i7 W. M{- l6 g1 ~7 z4 _0 K5 ^3 [
switch (command) {
; i, Y9 `% a, c6 T7 U. R w, p& `0 ? case COM_INIT_DB: .... break;
9 D$ a& x; O* h) w& T/ Y P+ I ...
+ t. O& D% L" i) ^% p# ?. s6 I& d case COM_QUERY: //查询语句: insert xxxx
x* B+ g! f& T' n mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
5 U( h" c; h) r: q5 F break;
2 F* s ?$ ^# K! H }: r3 Y2 @* }* Y2 _+ o( J
} Z2 J- o7 Z! h% C+ l' J
//sql解析模块
4 F6 {, U" h+ R# n/ k2 @void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)4 T, K+ J5 ]6 x: @! I* x
{- g9 ^! v* [0 J5 a' `
error= mysql_execute_command(thd);
% X% R ~& g- u9 ]2 G7 w* P}
/ g9 a6 e0 w* ]9 i9 Z
9 y6 J5 s' G' u: U' U& r4 w; V( K1 J5 I" f0 _4 [
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。5 f+ P0 U, c V( H# t
3 L A! T5 B1 U1 ]3 g8 W% [
//继续执行
: J. D) S: I% X# Gint mysql_execute_command(THD *thd)
3 ^' t i& E. @* ~7 L( e& V3 H* y{' p! N. [7 n1 ? p' n+ q
switch (lex->sql_command) 8 g4 F% L) H; K1 N
{6 U& [/ b% I9 {
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;: b# N/ N N1 m7 N( Q1 \% V
1 i7 l& U) K4 n5 l' ?0 l% H- u
//这个 insert 就是我要追的) b# M! h7 d- q
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
# M9 s5 ]! N+ S0 l5 Q% X lex->update_list, lex->value_list,5 z5 ^; ~4 ?( G% N: P k6 B3 ~
lex->duplicates, lex->ignore);
' V& D# _: m' [; a+ e$ P }
: F2 I) e9 H9 j2 ~" y}5 G: Y3 o/ W( I! l
//insert插入操作处理
4 d, S, G5 U8 n9 M5 abool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,' P7 G, s1 C9 T v {9 b) U
List<Item> &update_fields, List<Item> &update_values,
) ]# g; Z, d0 b( p) {& Z3 ? enum_duplicates duplic, bool ignore)
( J( t7 E3 @# {4 N: N{2 p# P, F% F0 g) J0 z
while ((values= its++))
4 |1 r2 b W8 q- P9 u {7 Y. S- }* ^4 k R" k
error= write_record(thd, table, &info, &update);3 a" j" @4 @2 `
}
5 \) e7 I9 ~" x2 ?9 k l) _ |}, o; E/ u3 [6 i# U$ B: k+ V
//写入记录' |! @. i2 @! f4 _. }
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)3 ^4 H1 F" s2 m, v8 {
{; k. x- y+ o$ u* O: p; b8 s! n
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)8 K2 ^7 B7 G `
{
( B1 u0 o. M, k. u/ i" c // ha_write_row 重点是这个函数
% F# g) J& x: B4 S0 s8 X4 f- o while ((error=table->file->ha_write_row(table->record[0])))
9 P M {9 p, J% | w, {3 Z {4 L( N' Q. _7 x6 H4 t
....
* w# n9 V8 n! U# g' [' x }4 a/ {: E9 J y% {, L
}
$ T9 w( k( Z8 E}
% j$ p5 k e) _& z c
& n1 z6 r& j1 J' t
- }; x/ d' R) W; L' E! v
& ]; D) J$ g7 f; ` [2 B5 f7 ^可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
9 v6 E, o: {9 m/ T9 N& \; Q, W4 Z' K9 a
<3> 继续挖 ha_write_row
8 R9 Y5 X7 K; a1 ?) E* x8 R- |9 Z6 J' X n
int handler::ha_write_row(uchar *buf)7 ?/ I- n+ K7 j1 ~6 w% R r
{4 _0 i/ @5 H! R- m
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })9 c0 C2 Q2 Y8 |% d: S: f2 j; G
}
4 \) w) ^0 f. ~+ t% P% z& @# a/ n0 k0 \
//这是一个虚方法
$ V M8 U7 S" i4 m$ b( J3 `virtual int write_row(uchar *buf __attribute__((unused))): t# e9 d3 t; o M* u% l+ I
{
2 {' r% P, l, t' I5 F return HA_ERR_WRONG_COMMAND;. T6 g1 A$ r2 ]! n9 }6 J
}
* H0 R9 Q5 C- L
& _) ?- B( A9 D2 _' x' T4 D; e: ]4 f2 S# I( F$ Q1 v
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁1 V& p& ]4 ]3 i* [7 \
& Q. r S$ |# F- P1 z4 `3. 调用链图) `7 C% t! N; K( ~/ ~: Q! w
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。) \% [+ ]3 t2 i; B
![]()
. {+ @ L2 ?* ?6 G6 [8 Y8 u
3 U3 Q/ m* l8 n+ {, r' {' b: ~4 r. T: C: q
三:总结
( P. G' `1 ?! u7 W. h9 \- y* X大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。/ u3 T) I) p4 {; [, {* k
————————————————
6 G0 l; |$ Z: [6 w3 t8 \1 W版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
' ?) f- M1 K6 u: Y原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
2 ` `) G v; u: H( u- z+ }, g |
zan
|