日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql global temporary table_【转】MySQL Temporary Table相关问题的探究

發(fā)布時(shí)間:2024/9/19 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql global temporary table_【转】MySQL Temporary Table相关问题的探究 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文轉(zhuǎn)載自:http://itindex.net/detail/10901-mysql-temporary-table

問題的引入

讓我們先來觀察幾條非常簡單的MySQL語句:

mysql> create temporary table tmp(id int, data char(20));

Query OK, 0 rows affected (0.00 sec)

mysql> create table tmp(id int, data char(20));

Query OK, 0 rows affected (0.00 sec)

mysql> drop table tmp;

Query OK, 0 rows affected (0.00 sec)

mysql> drop table tmp;

Query OK, 0 rows affected (0.00 sec)

mysql> drop table tmp;

ERROR 1051 (42S02): Unknown table 'tmp'

這是丁奇提出的引導(dǎo)性的問題,幾條語句看似簡單,不過接下來我們提出的一連串問題與進(jìn)

行的研究可都是圍繞它們來的!

看到以上語句,你很容易會(huì)產(chǎn)生類似于以下的疑問:

1. 上述語句在一個(gè)session中先后創(chuàng)建了兩個(gè)名為’tmp’的table,只不過一個(gè)是temporary

table,一個(gè)是normal table。問題來了:temporary table為何可以與同名的normal table

共存?

2. 上述語句成功執(zhí)行了兩條DROP TABLE語句,那么每一條語句操作的對象是哪個(gè)table呢?

亦即同名的temporary table與normal table之間的優(yōu)先級關(guān)系是如何的?

很好,有了問題就知道了前進(jìn)的方向!接下來我們就從這兩個(gè)問題入手,由淺入深,開始我

們的探索之旅吧!

單機(jī)模式下的同名問題與優(yōu)先級問題的探究

我們不妨從現(xiàn)象入手,先來驗(yàn)證第二個(gè)問題的結(jié)果究竟如何,即哪個(gè)表擁有較高的優(yōu)先級?

為此我們設(shè)計(jì)如下的語句:

mysql> create temporary table tmp(id1 int, data1 char(20));

Query OK, 0 rows affected (0.00 sec)

mysql> describe tmp;

+-------+----------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+-------+----------+------+-----+---------+-------+

| id1 | int(11) | YES | | NULL | |

| data1 | char(20) | YES | | NULL | |

+-------+----------+------+-----+---------+-------+

2 rows in set (0.00 sec)

mysql> insert into tmp values(1, "Some");

Query OK, 1 row affected (0.00 sec)

mysql> select * from tmp;

+------+-------+

| id1 | data1 |

+------+-------+

| 1 | Some |

+------+-------+

1 row in set (0.00 sec)

mysql> create table tmp(id2 int, data2 char(20));

Query OK, 0 rows affected (0.00 sec)

mysql> describe tmp;

+-------+----------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+-------+----------+------+-----+---------+-------+

| id1 | int(11) | YES | | NULL | |

| data1 | char(20) | YES | | NULL | |

+-------+----------+------+-----+---------+-------+

2 rows in set (0.00 sec)

mysql> insert into tmp values(2, "Some");

Query OK, 1 row affected (0.00 sec)

mysql> select * from tmp;

+------+-------+

| id1 | data1 |

+------+-------+

| 1 | Some |

| 2 | Some |

+------+-------+

2 rows in set (0.00 sec)

以上語句做的工作很簡單:先創(chuàng)建一個(gè)名為’tmp’的temporary table,并insert一個(gè)值;

之后創(chuàng)建一個(gè)名為’tmp’的normal table,也insert一個(gè)值。最終select時(shí)發(fā)現(xiàn),兩次

insert操作均作用于temporary table。

至此我們可以得到初步的印象是,同名的temporary table與normal table共存時(shí),

temporary table較高的優(yōu)先級。但是別忘了還存在另一種情況:先創(chuàng)建的表總有著較

高的優(yōu)先級。這個(gè)猜想是很容易來驗(yàn)證它的對錯(cuò)的,我們只需將剛才的創(chuàng)建表的順序調(diào)

換一下即可。這里就不再重復(fù)代碼,直接給出結(jié)果:即使temporary table在normal table

之后創(chuàng)建,諸如select,insert,update等操作仍然優(yōu)先作用于temporary table之上。

于是我們可以進(jìn)一步猜測drop表的時(shí)候,先drop的也是temporary table。

馬上來驗(yàn)證一下:

/* 緊接著之前的代碼 */

mysql> drop table tmp;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from tmp;

Empty set (0.01 sec)

mysql> describe tmp;

+-------+----------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+-------+----------+------+-----+---------+-------+

| id2 | int(11) | YES | | NULL | |

| data2 | char(20) | YES | | NULL | |

+-------+----------+------+-----+---------+-------+

2 rows in set (0.00 sec)

mysql> drop table tmp;

Query OK, 0 rows affected (0.00 sec)

mysql> show tables;

Empty set (0.00 sec)

mysql> describe tmp;

ERROR 1146 (42S02): Table 'test.tmp' doesn't exist

沒問題吧!到這里我們已經(jīng)從現(xiàn)象得出了初步的結(jié)論:在同一個(gè)session下同名的

temporary table與normal table共存時(shí),temporary table總是優(yōu)先被操作的。那么

我們可以更進(jìn)一步提問:為什么temporary table的優(yōu)先級會(huì)高于normal table呢?

而且別忘了在本段開始時(shí)我們還提出了一個(gè)問題:為什么在同一session下同名的

temporary table與normaltable可以共存?眾所周知兩個(gè)同名的temporary table或

normal table都是不被允許的。我們可以先做出猜想:temporary table與normal table

是存儲(chǔ)在不同的位置的。這個(gè)猜想對嗎?要回答這些問題,我們必須到MySQL的源碼中

一探究竟,找尋答案了!

(我插幾句:作為一個(gè)不折不扣的MySQL菜鳥,剛拿到MySQL源碼時(shí)我就像拿到了天書,

除了膜拜之外根本不知道從何入手。經(jīng)過一段時(shí)間的摸爬滾打,我高興的發(fā)現(xiàn)我終于

窺得了其中的端倪,并深感“任務(wù)驅(qū)動(dòng)+gdb”是上手的好方法。MySQL完整源碼可以從

以下地址下載:http://dev.mysql.com/downloads/)

我們可以從創(chuàng)建一張表的流程入手,來探究這個(gè)過程(以下代碼中,如果沒有特別注明,

其注釋均為原碼注釋。)

對于語句

create temporary table tmp(id int, data char(20));

create table tmp(id int, data char(20));

定位到./sql/sql_parse.cc中的mysql_execute_command()函數(shù)。

case SQLCOM_CREATE_TABLE:

{

...

if ((res= create_table_precheck(thd, select_tables, create_table)))

goto end_with_restore_list;

...

/* regular create */

if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)

res= mysql_create_like_table(thd, create_table, select_tables,

&create_info);

else

{

res= mysql_create_table(thd, create_table->db,

create_table->table_name, &create_info,

&alter_info, 0, 0);

}

...

}

首先我們查看同文件中create_table_precheck()函數(shù)的實(shí)現(xiàn):

...

/*

For temporary tables we don't have to check if the created table exists

*/

if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) &&

find_table_in_global_list(tables, create_table->db,

create_table->table_name))

{

error= FALSE;

goto err;

}

...

而find_table_in_global_list()函數(shù)實(shí)質(zhì)上調(diào)用了./sql/sql_base.cc文件中的

find_table_in_list()函數(shù)。這個(gè)函數(shù)的功能就是去內(nèi)存中的全局table list中遍歷,

確認(rèn)是否已有同名的normal table存在。注意,對于temporary table,到這里為止是不做

重名檢查的。

繼續(xù)跟蹤到./sql/sql_talbe.cc中的mysql_create_table()函數(shù)。

開頭的注釋說的很清楚:

/*

Database and name-locking aware wrapper for mysql_create_table_no_lock(),

*/

這個(gè)函數(shù)實(shí)際上是對mysql_create_table_no_lock()的一個(gè)封裝,并且處理了一些加鎖

機(jī)制。我們繼續(xù)跟蹤到同文件的mysql_create_table_no_lock()函數(shù)。

...

/* Check if table exists */

if (create_info->options & HA_LEX_CREATE_TMP_TABLE)

{

path_length= build_tmptable_filename(thd, path, sizeof(path));

create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;

}

else

{

path_length= build_table_filename(path, sizeof(path) - 1, db, alias, reg_ext,

internal_tmp_table ? FN_IS_TMP : 0);

}

...

這里我們看到了一個(gè)關(guān)鍵函數(shù)build_tmptable_filename(),它位于./sql/sql_table.cc文件

中,這個(gè)函數(shù)是為temporary table命名的。在該函數(shù)內(nèi)部我們又看到如下一段關(guān)鍵代碼:

...

my_snprintf(p, bufflen - (p - buff), "/%s%lx_%lx_%x%s",

tmp_file_prefix, current_pid,

thd->thread_id, thd->tmp_table++, reg_ext);

...

有了以上這段代碼,temporary table的命名規(guī)則就非常清楚了,其中current_pid為16進(jìn)制

形式,thd->thread_id是Client的線程序號,thd->tmp_table就是臨時(shí)表序號了,而reg_ext

就是形如*.frm這樣的后綴。

現(xiàn)在我們回到函數(shù)mysql_create_table_no_lock(),緊接著剛才的代碼:

/* Check if table already exists */

if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) &&

find_temporary_table(thd, db, table_name))

{

// 如果找到重名的表,那么執(zhí)行這里的錯(cuò)誤處理代碼(非原注釋)

}

...

在上面這段代碼中我們又看到了一個(gè)關(guān)鍵函數(shù)find_temporary_table(),這個(gè)函數(shù)內(nèi)部是大

有文章的,它會(huì)去tmp_table list中去遍歷并檢查temporary table是否已經(jīng)存在。如果一切

沒有問題,那么繼續(xù)往下執(zhí)行:

...

if (rea_create_table(thd, path, db, table_name,

create_info, alter_info->create_list,

key_count, key_info_buffer, file))

...

這里我們可以看到rea_create_table()函數(shù)的功能是創(chuàng)建normal table的實(shí)際數(shù)據(jù)文件。

...

if (create_info->options & HA_LEX_CREATE_TMP_TABLE)

{

/* Open table and put in temporary table list */

if (!(open_temporary_table(thd, path, db, table_name, 1)))

{

(void) rm_temporary_table(create_info->db_type, path);

goto unlock_and_end;

}

thd->thread_specific_used= TRUE;

}

...

上面這段代碼是對temporary table操作的,其中open_temporary_table()函數(shù)打開一個(gè)

temporary table并將其加入thd->temporary_table隊(duì)列。繼續(xù)往下,在函數(shù)末尾看到一

句代碼:

error= write_create_table_bin_log(thd, create_info, internal_tmp_table);

進(jìn)入write_create_table_bin_log()函數(shù),上來就是一段非常清晰的注釋:

/*

Don't write statement if:

- It is an internal temporary table,

- Row-based logging is used and it we are creating a temporary table, or

- The binary log is not open.

Otherwise, the statement shall be binlogged.

*/

已經(jīng)說得很明白了,如果是內(nèi)部創(chuàng)建的temporary table或者Row-based binlog模式下

創(chuàng)建temporary table或者binlog功能未開啟,那么不寫binlog,其他情況下都會(huì)寫。

至此,MySQL一個(gè)典型的創(chuàng)建表的流程就走完了。總結(jié)上述代碼,我們可以回答第一個(gè)問題,

也就是同名normal table與temporary table共存問題。現(xiàn)在我們知道,normal table與

temporary table保存的位置是不同的,temporary table保存在thd->temporary_table隊(duì)列

中,而normal table是保存在全局的隊(duì)列中的,這樣同名的normal table與temporary table

就可以共存。并且,temporary table是相對于session的,因?yàn)閟ession結(jié)束后相應(yīng)的線程就

被回收了,那么對應(yīng)于該線程的temporary table也就被釋放了。更進(jìn)一步,從temporary

table的命名規(guī)則我們可以看到,每個(gè)temporary table都對應(yīng)著獨(dú)特的客戶端線程id,那么

顯然各個(gè)Client之間同名的temporary table是允許共存的。而normal table顯然是在任何情

況下都不允許同。

為了回答第二個(gè)問題,即優(yōu)先級問題,我們只需要看一下drop一個(gè)表的過程即可,其他操作

的原理也是類似的。這里我們就不再像剛才那么詳細(xì)的一步步分析源碼,直接給出關(guān)鍵代碼

(位于函數(shù)mysql_rm_table_part2()中,該函數(shù)位于./sql/sql_table.cc)

...

error= drop_temporary_table(thd, table); // 這里刪除臨時(shí)表(非原注釋)

...

error= ha_delete_table(thd, table_type, path, db, table->table_name,

!dont_log_query); // 這里刪除表的內(nèi)容和索引(非原注釋)

...

/* Delete the table definition file */

strmov(end,reg_ext);

// 以下刪除表的定義文件(非原注釋)

if (!(new_error=my_delete(path,MYF(MY_WME))))

{

some_tables_deleted=1;

new_error= Table_triggers_list::drop_all_triggers(thd, db,

table->table_name);

}

...

從以上代碼我們不難看出,drop表的過程總是先走temporary table,再走normal table的。

這也就解釋了為何temporary table有著比normal table更高的優(yōu)先權(quán)。

好了,到目前為止我們已經(jīng)從本質(zhì)上回答了文章開頭提出的兩個(gè)問題,這樣看起來問題已經(jīng)

解決的比較圓滿了。但是且慢,我們以上所做的探究全部基于同一臺服務(wù)器下,如果是分布

式的系統(tǒng),即主從模式下,又會(huì)出現(xiàn)什么樣的狀況呢?下面一節(jié)我們繼續(xù)探究。

主從模式下temporary table機(jī)制的探究

首先我們要說明的是MySQL主從備份的實(shí)現(xiàn)機(jī)制。我們知道MySQL的眾多日志類型中有一種為

binlog日志類型,凡是涉及到修改數(shù)據(jù)庫的操作都會(huì)被記錄到binlog日志中。binlog日志本

身又分為兩種記錄方式:Statement-based方式,Row-based方式(Mixed方式可以視為這兩種

方式的混合)。在主從模式下,某個(gè)特定的分布式服務(wù)器群中有兩種服務(wù)器:Master(主服務(wù)

器)與Slave(從服務(wù)器)。Master方將自己的數(shù)據(jù)修改痕跡以某種方式記錄在本機(jī)的binlog文

件中,當(dāng)有Slave連接到Master時(shí),Master會(huì)啟動(dòng)Binlog dump線程來將本地的binlog內(nèi)容發(fā)

送給Slave方。此時(shí)Slave方會(huì)啟動(dòng)兩個(gè)線程:Slave I/O線程和Slave SQL線程。Slave I/O

線程讀取從Master的Binlog dump線程發(fā)送過來的binlog內(nèi)容,并將其寫入本機(jī)的Relay log

中。Slave SQL線程則從本地的Relay log讀取并且執(zhí)行需要更新的事件。更具體的實(shí)現(xiàn)與配

置細(xì)節(jié)可以參考官方文檔:http://dev.mysql.com/doc/refman/5.1/en/replication.html

注意到Slave方執(zhí)行事件的線程只有一個(gè),那就是Slave SQL線程。想一想按照我們目前的理

解,會(huì)出現(xiàn)怎樣的問題?回憶剛才的MySQL temporary table命名規(guī)則,其中有一項(xiàng)是線程

id。再回憶剛才我們說到,由于temporary table是相對于session的,于是不同的Client可

以創(chuàng)建同名的temporary table。問題來了:將這個(gè)情景移到主從模式下,Master方同時(shí)連

接了兩個(gè)Client,每一個(gè)Client各自創(chuàng)建了一個(gè)名為a的temporary table。我們假設(shè)此時(shí)

Master的binlog模式被設(shè)置為Statement-based,那么這兩個(gè)建表事件都會(huì)被寫入binlog。

現(xiàn)在Slave I/O線程檢測并讀取了這兩個(gè)事件,Slave SQL線程要執(zhí)行這兩個(gè)事件了。按照

我們的想法,此時(shí)Slave是不能區(qū)分這兩個(gè)temporary table的,因?yàn)榫€程id相同!

但是經(jīng)過實(shí)際驗(yàn)證,MySQL能處理這個(gè)問題,而并沒有像我們預(yù)想的那樣會(huì)報(bào)錯(cuò)。那么MySQL

內(nèi)部是如何處理的呢?讓我們再仔細(xì)讀一下建表函數(shù)mysql_create_table_no_lock()中的檢

查temporary table名字沖突的函數(shù)find_temporary_table()的實(shí)現(xiàn)代碼。

...

key_length= create_table_def_key(thd, key, table_list, 1);

...

顯然create_table_def_key()函數(shù)是區(qū)分每個(gè)temporary table的關(guān)鍵,我們繼續(xù)看這個(gè)函數(shù)

內(nèi)部的細(xì)節(jié):

...

int4store(key + key_length + 4, thd->variables.pseudo_thread_id);

...

這里我們看到一個(gè)關(guān)鍵信息:thd->variables.pseudo_thread_id。如果使用gdb調(diào)試,我們發(fā)

現(xiàn)在find_temporary_table()函數(shù)中thd->variables.pseudo_thread_id的值等于Relay-log中

的線程id,也就是Master的binlog中記錄Client的線程id的值。然而注意到Slave SQL線程初

始化函數(shù)handle_slave_sql()中調(diào)用的 init_slave_thread()函數(shù)中有這樣一句代碼:

...

thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;

...

在這里,thd->variable.pseudo_thread_id是被初始化為Slave當(dāng)前線程id的。那么它是何時(shí)被

修改的呢?繼續(xù)看代碼:

...

while (!sql_slave_killed(thd,rli))

{

...

if (exec_relay_log_event(thd,rli))

{

...

}

}

...

以上代碼進(jìn)入了執(zhí)行relay log的循環(huán)。exec_relay_log_event()中調(diào)用了函數(shù)

apply_event_and_update_pos(),而這個(gè)函數(shù)中調(diào)用了ev->apply_event(),最終調(diào)用了

Query_log_event::do_apply_event()。在該函數(shù)中我們看到:

...

thd->variables.pseudo_thread_id= thread_id; // for temp tables

...

就是在這里,thd->variables.pseudo_thread_id已經(jīng)被置為我們想要看到的值了。很神奇吧!

主從模式下temporary table可能造成的不同步問題

現(xiàn)在我們來考慮另外一個(gè)問題,即主從模式下temporary table可能引起的主從間不同步問

題。

回憶MySQL創(chuàng)建temporary table過程。該過程除了將temporary table信息加入當(dāng)前線程所

擁有的temporary table隊(duì)列之外,還做了一項(xiàng)工作,即在/tmp目錄下創(chuàng)建了臨時(shí)數(shù)據(jù)文件,

如:

#sql64d6_18_0.frm #sql64d6_18_0.ibd (InnoDB下)

考慮以下情形:Master機(jī)上創(chuàng)建了一個(gè)temporary table,并且此時(shí)binlog模式為

Statement-based。于是Slave上讀到了這個(gè)事件,并且在Slave上也同步了這個(gè)操作,即同樣

建立了一個(gè)temporary table。此時(shí)由于某種原因,Slave突然意外重啟。我們知道服務(wù)器

重啟會(huì)導(dǎo)致所有/tmp文件夾下的數(shù)據(jù)文件被清空,那么在Slave上,原先的temporary table

不復(fù)存在。但是此時(shí)Master上的原始的temporary table還是好好的!這樣,如果我們在

Master上做任何對該temporary table上的修改操作都會(huì)引起Slave端報(bào)錯(cuò),產(chǎn)生類似以下信息:

Error 'Table 'test.tmp' doesn't exist' on query. Default database: 'test'.

Query: 'insert into tmp values(SomeValue)'

我們知道在Slave Server關(guān)閉后直到重啟前,/tmp目錄下的數(shù)據(jù)文件都是存在的。問題的本質(zhì)

在于:Slave Server關(guān)閉后,內(nèi)存中的temporary table鏈表被回收,導(dǎo)致/tmp下的數(shù)據(jù)文件

沒有對應(yīng)的數(shù)據(jù)結(jié)構(gòu),那么我們也就無從知曉對應(yīng)的創(chuàng)建該表的Client到底是哪一個(gè)。

解決這個(gè)問題的基本思路就是在Slave重啟時(shí)以某種方式恢復(fù)原先內(nèi)存中的相關(guān)信息。其中一種

思路是,在Slave創(chuàng)建temporary table時(shí),我們額外寫一個(gè)文件來記錄與維護(hù)數(shù)據(jù)文件與客戶

端線程id、表名、數(shù)據(jù)庫名的對應(yīng)關(guān)系。另外一種思路是,在Slave創(chuàng)建temporary table時(shí),

我們將相應(yīng)的binlog記錄下來,然后在啟動(dòng)的時(shí)候重做這些記錄。具體的實(shí)現(xiàn)這里就不再詳細(xì)

展開。

玩的開心!

與50位技術(shù)專家面對面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的mysql global temporary table_【转】MySQL Temporary Table相关问题的探究的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。