- 在线时间
- 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 n; w9 ^* Z6 |; C1. 讲故事
# v! `( V/ h, k. O/ M0 x6 d最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。1 W- V+ r# a1 H$ F. |- j9 P% U
( _4 j4 K1 F' u6 P/ O. \, Q
二:了解架构图
; t4 J4 o l4 G; Hmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。" Z; X% t/ |- [( Y
: O$ X! }5 ~5 J0 c+ ^" P, }# l. T1. 从架构图入手9 [- p S8 x3 X4 u- f/ R
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。% E4 H/ R2 T# h9 H; R. ?
![]()
9 e' g% Z$ B9 b: Z0 L; E
5 l: @* I& Q/ X5 P6 O$ d% j$ q, N& r- Q) m: |7 S3 T- Z/ G8 E
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
( C+ O. f9 w! h1 I5 J" w5 |
% g% M4 u5 l; h3 ]! d# v2. 功能点介绍" U% `9 u- M9 Q" J' D5 D6 q
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
6 E6 D# z& p' d* z- C% d8 q B* O( N( Z) u: o* ]% Y) p0 E
<1> Client
) j1 O8 i1 H) d6 b9 B* m不同语言的sdk遵守mysql协议就可以与mysqld进行互通。" ?: r% t: A" k2 f0 K
2 K$ `4 r5 W% d5 o* r; u( _' E$ k8 u<2> Connection/Thread Pool
$ p ~: m; ?' ^& O: _MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
8 Y, T9 |/ c, F {& c
h5 O' C+ Z2 c) t6 z<3> SqlInterface,Parse,Optimizer,Cache
) s8 Q5 w! E, m/ `9 X$ ^7 b6 d! N对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
: g0 _8 h! U/ k: i7 c
9 f( H" Q8 N8 m' e1 i<4> Storage Engines
& `1 [- a# h7 D# g3 H D负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。3 E' s; H# T6 L1 r$ ?% J1 `
. P9 ~3 M/ e5 D三: 源码分析: t7 g( B) z( h% Y" F
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
# D3 v. Z" w) ] u$ }+ x5 j
+ e( t& w& q k* s0 y- S! W5 }1. 了解mysql是如何启动监听的
4 ^" v5 p2 y. y# W手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
+ o' n% |% k! @: ^![]()
! t; N. m) d. U" Z- A7 \
6 p' G, U% j9 U, N. p! g: h) v; B. J7 F5 @
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
1 x6 Y/ ? E0 ] g i
2 W( I7 Y- _1 G1 W: h6 G8 L2 Z/ L<1> mysqld_main 入口函数 => sql/main.cc2 y5 ^4 v" z+ a! a4 E- ]/ U7 q
0 Z. U7 ?* R. y; X. p
0 N0 \; Q2 ]) ? W# [, h8 I( O
extern int mysqld_main(int argc, char **argv);0 x% v: ]9 A$ J' m, G( s v
: a! c0 }# I% W' x: Z# u
int main(int argc, char **argv) D2 [2 ]+ o( [/ \. k; P; e
{/ p" q! b- h) i. [6 w" R
return mysqld_main(argc, argv);* O& ]6 X% F5 z! i
}
! N: d8 ?9 ^* \3 c I( j
# h& W( l7 l! H
; Z4 ?9 |7 ]0 j这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
2 H" X* S# s& R1 x) y% H9 G) O9 J% `: u2 X! u7 B
<2> 创建监听6 H7 C. i, g4 ]1 \8 p
, F- E4 T4 S* q; m O. Z$ N; R4 a
/ L6 k! A) Z& r% I( W' { nint mysqld_main(int argc, char **argv)
/ Q, j6 h) e+ i# Z/ m# O- z& K2 v{$ r; t7 N: ^7 _6 w
//创建服务监听线程
3 S+ D+ ]. k/ Q7 I* X handle_connections_sockets();; G: `! D. I! ~* k
}
0 H8 D; Q4 e* E' ]: A$ x' t5 s5 b% h7 M6 w7 {5 t. B6 w4 g
void handle_connections_sockets()4 G; P, R+ g% r# P
{
: B/ g& `- K7 d, p& \* g- G5 c //监听连接6 }* G! e8 q7 W
new_sock= mysql_socket_accept(key_socket_client_connection, sock,7 a5 i% ^" I% H3 `+ M, m
(struct sockaddr *)(&cAddr), &length);9 U( Z0 J7 T$ [
3 E) T% G4 o" y% g& X
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
1 n& y; {8 o! D thd->security_ctx->set_host((char*) my_localhost);6 p# n7 r2 H2 g9 }
8 ?% n7 f% r* B7 d+ N9 L3 I/ u //创建连接5 f+ m4 }) Z7 S' X6 k- ]
create_new_thread(thd);
8 Z) W* J0 e7 X- u9 f( O% w}
1 i- a6 @2 C, x- Q' ~1 x$ y' T- q) e- p& L$ R" p
//创建新线程处理处理用户连接
) w9 o3 k9 o+ P8 n) sstatic void create_new_thread(THD *thd){
# D/ D0 J/ b$ C* U- n2 ^9 N4 H. m9 u8 F5 @4 m! {# ?! H/ o
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
^0 `% A; e6 g3 T. ^7 P
. ]$ q# @& X4 F1 I- F$ { //线程进了线程调度器
+ ?$ i* i3 C6 @) | MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); 1 u, B: I; D! R* T
}
4 b& Q, A% t( s) c6 x- _2 L
, w* _6 }3 b; u8 z) ]6 @' u m8 j3 S
W3 [" Z1 M4 A3 }! U; h" n$ `, U至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
4 i5 A3 y, `3 O+ @8 @8 j2 ^9 Z( t( l6 _1 o. K
. l, |+ F2 T$ n0 x$ c- I2. 理解mysql是如何处理sql请求
$ T9 J& g6 x- R( S9 A这里我以Insert操作为例稍微解剖下处理流程: H3 U6 p- ?: |, K2 B
: I/ w# W; a) t$ N
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。* Q5 f! w0 p# C# v
1 x! K; k/ ?4 a/ R2 R0 M0 Z6 `, Q: o5 r+ C8 `5 ?
static scheduler_functions one_thread_per_connection_scheduler_functions=/ f, b; M! @' P* x! e) M
{
9 r! ^% C3 a8 K1 Y. ] n) B 0, // max_threads
E z; O5 o3 y; _4 O NULL, // init
# u% R9 a q+ E1 a% l5 G init_new_connection_handler_thread, // init_new_connection_thread
% O& H2 D% t/ @( x3 z+ Z4 ~6 E create_thread_to_handle_connection, // add_connection; ~9 R* m0 q" m- S
NULL, // thd_wait_begin
' r1 ^; l! ]* X; D, R! }) C) t NULL, // thd_wait_end; ?; q* ^5 _2 R7 y
NULL, // post_kill_notification
$ n1 z, g/ z2 R$ q, } one_thread_per_connection_end, // end_thread
4 m( Q6 H, v! N# l NULL, // end
8 ]" U$ O9 U4 p, J};: U1 p5 c: ~+ L
1 c+ @8 t Y; B: ^1 o$ L% N; D( F3 E
# M8 w4 q% A9 Y4 Z从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
6 I$ Q% l$ r* V5 V
8 N6 N6 B, G e+ {; r. i( [<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪- I6 Q$ g A4 P" L/ Q9 }; r8 }" F
* n4 m3 t4 o9 e' t# j6 ?0 y
void create_thread_to_handle_connection(THD *thd)0 L2 L O" |5 c2 b* h
{
! g8 O1 ]2 \, J3 V) z# i" q- E: \ if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
" q0 J, w' j1 K) S) x( H7 S handle_one_connection,(void*) thd))){}
: `. Q+ K, D- l9 n- `}/ W1 Y6 ]6 h- U# r: }( L% b
//触发回调函数 handle_one_connection
1 B2 r# y: t) z: I" g! Q' `1 I8 Fpthread_handler_t handle_one_connection(void *arg)
: a7 v$ q: X' J4 ~2 M{
/ b+ X+ R; N0 `9 C+ |* f4 r do_handle_one_connection(thd);
5 N, ?+ R( W& z$ B! i% n}/ S1 G; B' m/ R0 Z1 ~
//继续处理
7 v& F7 ]( G5 W8 v+ Gvoid do_handle_one_connection(THD *thd_arg){
. Y( F& T2 G$ D( `; c( f while (thd_is_connection_alive(thd))' B9 G L/ g. [. t3 h: f$ A
{
3 e3 V3 h. i9 B% b/ s mysql_audit_release(thd);
6 W! v/ _; n8 e- F1 W* p; h if (do_command(thd)) break; //这里的 do_command 继续处理
" m! E5 q" x" @" B$ i }
5 G' U8 I1 e: E3 b4 h0 G3 Y}
6 m# g- B w! {, X* U6 i//继续分发0 ?9 f! w+ t! Z
bool do_command(THD *thd)
1 I6 c# {7 x# G) z! O( H9 V{
0 C, s+ Z# e2 } return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
# Y' B4 r$ V7 ?$ {% _}" V$ \% ^& t0 v, s( s9 L
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length), Q1 X9 ~4 Z7 T0 w' |# c Z
{/ ~' O+ i! B( w I. _- [
switch (command) {
( C) s% l I; L b, T+ S case COM_INIT_DB: .... break;
2 D' V6 V8 x9 s4 j/ Z; @3 }$ v7 ] ...' C( ?6 q- V$ S0 Y0 A& V
case COM_QUERY: //查询语句: insert xxxx, H$ Y+ `% K6 |/ O, [
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
7 ?0 H. \3 F9 \6 _8 c/ j break;9 E* [6 |5 C0 |) g, q* o4 c
}9 w+ d+ i$ M$ p* i; P( G8 q
}
. Y2 S& [) V8 d, I1 f" S3 W+ X//sql解析模块$ _9 f; f" c$ x0 G) p& v& Q2 Z( `
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)* E2 z/ q/ h; h/ z5 X" x7 i
{
8 o: z' S% o) S9 D) T error= mysql_execute_command(thd);
{. a. L* I, e3 d; S}$ s+ v$ T7 x6 n
. i8 R+ H! D6 ?5 `7 B" \6 j6 T" y1 m2 {' I) S* \, M0 B: m2 u
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
! I. |, @ a/ |
2 Y9 o) w6 t; a; T- f/ g( c! |//继续执行
- |+ Y1 J4 R& ^% nint mysql_execute_command(THD *thd)
* T, h! C$ P w! A{
% i5 d6 F, [% K+ A' @ switch (lex->sql_command) ' T7 m: P, O0 @6 [
{& ]1 X+ k+ P* V) }2 _7 S$ e- [
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
$ s5 M$ \3 e( z* z8 Y) u- A8 k5 B0 P7 w: V* D1 a) H l8 t
//这个 insert 就是我要追的
: M* z9 ?! V' u2 r case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,3 a% ~7 A6 x$ {8 o# d& S. {
lex->update_list, lex->value_list,
0 g! l3 a* j5 g8 W7 T: l C6 J lex->duplicates, lex->ignore);
- b8 J7 T3 ?1 t. [" y9 M8 ^ } S7 t* Z7 ^+ @2 y
}
3 z$ T& u' Z4 W//insert插入操作处理7 a7 m/ Y. P9 v9 H9 F) z
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
5 }) P4 k$ V5 P+ i; \* F7 p2 Q List<Item> &update_fields, List<Item> &update_values, / {$ O# o7 ~* ]: J/ t8 F/ ^
enum_duplicates duplic, bool ignore)
$ K7 }; x- I' `7 Q{
% ~8 c$ D- f& k+ f1 ^ while ((values= its++))
6 a3 j2 i1 o) N. l {& m5 E I; R) R& s$ ?
error= write_record(thd, table, &info, &update);
: }% G3 w; \$ p6 s9 ~' [ }
( D8 F* p8 |% k: a' G}* ]5 ^( K: f" S
//写入记录
; }# w: k$ J: h( p; `) V* [3 L$ z ?+ {int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)! [+ d; H% [5 z4 |& C1 V
{
3 H, R& o. H, j3 V if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
* P9 u/ c3 [& i, Z; r1 h {
) m" z# R6 g% h" h: T // ha_write_row 重点是这个函数
1 y8 r8 N$ [1 V* F# V O, V while ((error=table->file->ha_write_row(table->record[0])))2 s" {) Q3 e7 n8 G3 i/ [
{
+ {( ], |/ B+ D; n ....- L2 i6 h8 v2 V/ q- H' @
}# z) d) Y8 F: w2 w, w _+ W
}
7 U/ n* d1 ]3 z' N, R* z' ~}
" m u7 {' x% O4 p" B+ p) U/ M, u2 c/ E
% r6 K, ?2 E T& b6 h% d3 ?
8 @/ i- S2 H' q) y- @6 m
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。, E- i- H7 E' f9 E. m
1 W. W) j# N* m5 @
<3> 继续挖 ha_write_row
0 h: q1 |' s; Q, Y' m2 c% O" F e Y/ o T8 }" m/ o
int handler::ha_write_row(uchar *buf); [' D: o7 R( R$ j6 c( M) J: D
{ K" X. p3 O( h7 H; k7 L
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
) _ Y) {, U7 x( E1 J! D}
& a5 O$ |6 M0 d" ^; O4 I4 Y) q( @3 t [+ ^+ w
//这是一个虚方法0 E7 I( \( m+ Y
virtual int write_row(uchar *buf __attribute__((unused)))
. I: z& |& u& `! `{$ r( W' \4 e. S8 m. h
return HA_ERR_WRONG_COMMAND;. A* A' Z/ `6 y# H0 r7 \
}
% u0 f6 X! K: E/ I9 |( ^
3 c$ y3 h7 }# A( J+ j1 `. ]$ ?4 h6 A2 ~: k6 J: F) E1 b) ]) w* z: p7 ?2 V* p
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁! M) u' u, Y; @0 H/ e; Y
% x; M, e4 e% ]( q" g! D
3. 调用链图
6 @: B6 d5 ^( F* P- r+ h这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
( U) |# c& U; I& r: \& e![]()
: _ P0 T( ^( o. Y
3 l/ L4 t. l9 B1 n
* n, W; |; @/ o三:总结
7 j- Y8 n% Z, P+ i* t大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
' C3 X5 q/ \ I6 I+ s- q' m2 i ~, d————————————————4 c6 E% x8 B6 J( [
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。. J- s& Z' z$ |% B* T0 v
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415+ r z8 @) K2 U+ W
|
zan
|