mysql dba系統學習(20)mysql儲存引擎MyISAM

dapan發表於2021-09-09


mysql儲存引擎MyISAM

1,建立myisam表

mysql> create table t (id int , name varchar(30) , msg varchar(100)) engine = MyISAM;

mysql> show table status like "t" G ;

*************************** 1. row ***************************

Name: t

Engine: MyISAM

Version: 10

Row_format: Dynamic

Rows: 0

Avg_row_length: 0

Data_length: 0

Max_data_length: 281474976710655

Index_length: 1024

Data_free: 0

Auto_increment: NULL

Create_time: 2013-09-12 00:39:29

Update_time: 2013-09-12 00:39:29

Check_time: NULL

Collation: utf8_general_ci

Checksum: NULL

Create_options:

Comment:

1 row in set (0.00 sec)

2,auto_increment

當使用這個引數的時候,這個列一定要是主鍵

mysql> create table tt (id int auto_increment primary key  , name varchar(30) , msg varchar(100)) engine = MyISAM;

Query OK, 0 rows affected (0.01 sec)

mysql> insert into tt(name,msg) values('chenzhongyang','good');

Query OK, 1 row affected (0.00 sec)

雖然我們沒有指定名字是chenzhongyang的id是1,但是有了auto_increment這個引數,系統會自動給他加上1

mysql> select * from tt;

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

| id | name          | msg  |

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

|  1 | chenzhongyang | good |

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

1 row in set (0.01 sec)

我們還可以設定auto_increment的值,但是這個值設定了的話,就會從這個值開始累積

mysql> alter table tt auto_increment=2000;

Query OK, 1 row affected (0.02 sec)

Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into tt(name,msg) values('tianhongyan','baby');

Query OK, 1 row affected (0.00 sec)

mysql> select * from tt;

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

| id   | name          | msg  |

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

|    1 | chenzhongyang | good |

| 2000 | tianhongyan   | baby |

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

2 rows in set (0.00 sec)

mysql> insert into tt(name,msg) values('zhongguo','XXXXXXX-YYYYYYYYY-+VVVV');

Query OK, 1 row affected (0.00 sec)

mysql> select * FROM tt;

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

| id   | name          | msg                     |

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

|    1 | chenzhongyang | good                    |

| 2000 | tianhongyan   | baby                    |

| 2001 | zhongguo      | XXXXXXX-YYYYYYYYY-+VVVV |

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

3 rows in set (0.00 sec)

還有一個函式比較有用last_insert_id()。這個函式可以查出最後一次insert的id

mysql> select  last_insert_id();

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

| last_insert_id() |

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

|             2001 |

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

1 row in set (0.00 sec)

3,儲存結構

資料檔案(.MYD),索引檔案(.MYI)和結構檔案(.frm)

特點:可以在不同伺服器上複製資料檔案和索引檔案。

如果我們把索引檔案和資料檔案放到不同的機器上,那麼可以提高系統i/o

4,不支援事務

即使我們關閉autocommit,myisam引擎還是會立即執行我們的命令,這個時候rollback已經沒有用了

mysql> show variables like "%autocommit%";

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

| Variable_name | Value |

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

| autocommit    | ON    |

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

1 row in set (0.00 sec)

mysql> set autocommit=OFF ;

Query OK, 0 rows affected (0.00 sec)

mysql> show variables like "%autocommit%";

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

| Variable_name | Value |

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

| autocommit    | OFF   |

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

1 row in set (0.00 sec)

mysql> delete from tt where id=1;

Query OK, 1 row affected (0.00 sec)

mysql> rollback;

Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select   * from tt;

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

| id   | name        | msg                     |

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

| 2000 | tianhongyan | baby                    |

| 2001 | zhongguo    | XXXXXXX-YYYYYYYYY-+VVVV |

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

2 rows in set (0.00 sec)

5,myisam_data_pointer_size

預設的指標大小是6byte,一個位元組是8bit那麼資料檔案的大小就是2的6*8次方byte

也就是1024*1024*1024*1024*256/1024/1024/1024/1024=256TB

mysql> show variables like "%pointer%";

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

| Variable_name            | Value |

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

| myisam_data_pointer_size | 6     |

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

1 row in set (0.00 sec)

我們來做個實驗試試

如果myisam_data_pointer_size=2,那麼就意味著一個表的最大資料檔案是65535/1024=64K

mysql> set global myisam_data_pointer_size=2;

Query OK, 0 rows affected (0.00 sec)

mysql> show variables like "%pointer%";

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

| Variable_name            | Value |

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

| myisam_data_pointer_size | 2     |

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

1 row in set (0.00 sec)

我們來建立一個 大表ss

mysql> create table ss select * from information_schema.tables ;

Query OK, 54 rows affected (0.09 sec)

Records: 54  Duplicates: 0  Warnings: 0

mysql> insert into ss select * from ss;

Query OK, 108 rows affected (0.01 sec)

Records: 108  Duplicates: 0  Warnings: 0

mysql> insert into ss select * from ss;

Query OK, 216 rows affected (0.01 sec)

Records: 216  Duplicates: 0  Warnings: 0

這個時候出現了表ss滿了的錯誤,我們看看資料檔案 是64K,要想繼續可以插入資料,那麼就要把這個引數調大

mysql> insert into ss select * from ss;

ERROR 1114 (HY000): The table 'ss' is full

mysql> insert into ss select * from ss;

ERROR 1114 (HY000): The table 'ss' is full

mysql> insert into ss select * from ss;

ERROR 1114 (HY000): The table 'ss' is full

[root@test3 test]# ls -lh

total 116K

-rw-rw----. 1 mysql mysql 9.3K Sep 12 06:44 ss.frm

-rw-rw----. 1 mysql mysql  64K Sep 12 06:44 ss.MYD

-rw-rw----. 1 mysql mysql 1.0K Sep 12 06:44 ss.MYI

mysql> insert into ss select * from ss;

ERROR 1114 (HY000): The table 'ss' is full

mysql> alter table ss max_ROWS=10000000000 ;

Query OK, 496 rows affected (0.11 sec)

Records: 496  Duplicates: 0  Warnings: 0

mysql> insert into ss select * from ss;

Query OK, 496 rows affected (0.02 sec)

Records: 496  Duplicates: 0  Warnings: 0

6,myisam的儲存行格式

MyISAM支援三種不同儲存格式。

其中兩個(固定格式和動態格式)根據正使用的列的型別來自動選擇。第三個,即已壓縮格式,只能使用myisampack工具來建立。

1.fixed靜態格式(固定長度)表的一般特徵:

·CHAR列對列寬度是空間填補的。

·非常快。

·容易快取。

·崩潰後容易重建,因為記錄位於固定位置。

·重新組織是不必要的,除非你刪除巨量的記錄並且希望為作業系統騰出磁碟空間。為此,可使用OPTIMIZETABLE或者myisamchk-r。

·通常比動態格式表需要更多的磁碟空間。

2.dynamic動態格式表的一般特徵:

·除了長度少於4的列外,所有的字串列是動態的。

·在每個記錄前面是一個點陣圖,該點陣圖表明哪一列包含空字串(對於字串列)或者0(對於數字列)。注意,這並不包括包含NULL值的列。如果一個字元列在拖曳空間移除後長度為零,或者一個數字列為零值,這都在點陣圖中標註了且列不被儲存到磁碟。非空字串被存為一個長度位元組加字串的內容。

·通常比固定長度表需要更少的磁碟空間。

·每個記錄僅使用必需大小的空間。儘管如此,如果一個記錄變大,它就按需要被分開成多片,造成記錄碎片的後果。比如,你用擴充套件行長度的資訊更新一行,該行就變得有碎片。在這種情況下,你可以時不時執行OPTIMIZETABLE或myisamchk-r來改善效能。可使用myisamchk-ei來獲取表的統計資料。

·動態格式表在崩潰後要比靜態格式表更難重建,因為一個記錄可能被分為多個碎片且連結(碎片)可能被丟失。

3.已壓縮表有下列特徵:

·已壓縮表佔據非常小的磁碟空間。這最小化了磁碟用量,當使用緩慢的磁碟(如CD-ROM)之時,這是很有用的。

·每個記錄是被單獨壓縮的,所以只有非常小的訪問開支。依據表中最大的記錄,一個記錄的頭在每個表中佔據1到3個位元組。每個列被不同地壓縮。通常每個列有一個不同的Huffman樹。一些壓縮型別如下:

-字尾空間壓縮。

-字首空間壓縮。

-零值的數用一個位來儲存。

-如果在一個整型列中的值有一個小的範圍,列被用最小可能的型別來儲存。比如,一個BIGINT列(8位元組),如果所有它的值在-128到127範圍內,它可以被儲存為TINYINT列(1位元組)

-如果一個列僅有一小組可能的值,列的型別被轉化成ENUM。

-一個列可以使用先前壓縮型別的任意合併。

·可以處理固定長度或動態長度記錄。

7,加鎖和併發

MyISAM儲存引擎只支援表鎖,這也是MySQL開始幾個版本中唯一支援的鎖型別。隨著應用對事務完整性和併發性要求的不斷提高,MySQL才開始開發基於事務的儲存引擎,後來慢慢出現了支援頁鎖的BDB儲存引擎和支援行鎖的InnoDB儲存引擎(實際InnoDB是單獨的一個公司,現在已經被Oracle公司收購)。但是MyISAM的表鎖依然是使用最為廣泛的鎖型別

加鎖:對整張表進行加鎖,而不是行。

併發:在讀資料的時候,所有的表上都可以獲得共享鎖(讀鎖),每個連線都不互相干擾。

在寫資料的時候,獲得排他鎖,會把整個表進行加鎖,而其他的連線請求(讀,寫請求)都處於等待中。

可以透過檢查table_locks_waited和table_locks_immediate狀態變數來分析系統上的表鎖定爭奪:

mysql> show status like 'table%';

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

| Variable_name | Value |

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

| Table_locks_immediate | 2979 |

| Table_locks_waited | 0 |

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

2 rows in set (0.00 sec))

如果Table_locks_waited的值比較高,則說明存在著較嚴重的表級鎖爭用情況。

對MyISAM表的讀操作,不會阻塞其他使用者對同一表的讀請求,但會阻塞對同一表的寫請求;對MyISAM表的寫操作,則會阻塞其他使用者對同一表的讀和寫操作;MyISAM表的讀操作與寫操作之間,以及寫操作之間是序列的

MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,在執行更新操作(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖,這個過程並不需要使用者干預,因此,使用者一般不需要直接用LOCKTABLE命令給MyISAM表顯式加鎖。顯式加鎖基本上都是為了方便而已,並非必須如此。也正是因為這樣,所以myisam不會產生死鎖。

READ鎖表

獲得表film_text的READ鎖定

mysql> lock table film_text read;

Query OK, 0 rows affected (0.00 sec)

當前session可以查詢該表記錄

mysql> select film_id,title from film_text where film_id = 1001;

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

| film_id | title            |

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

| 1001     | ACADEMY DINOSAUR |

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

1 row in set (0.00 sec)

其他session也可以查詢該表的記錄

mysql> select film_id,title from film_text where film_id = 1001;

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

| film_id | title            |

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

| 1001    | ACADEMY DINOSAUR |

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

1 row in set (0.00 sec)

當前session不能查詢沒有鎖定的表

mysql> select film_id,title from film where film_id = 1001;

ERROR 1100 (HY000): Table 'film' was not locked with LOCK TABLES

其他session可以查詢或者更新未鎖定的表

mysql> select film_id,title from film where film_id = 1001;

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

| film_id | title         |

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

| 1001    | update record |

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

1 row in set (0.00 sec)

mysql> update film set title = 'Test' where film_id = 1001;

Query OK, 1 row affected (0.04 sec)

Rows matched: 1 Changed: 1 Warnings: 0

當前session中插入或者更新鎖定的表都會提示錯誤:

mysql> insert into film_text (film_id,title) values(1002,'Test');

ERROR 1099 (HY000): Table 'film_text' was locked with a READ lock and can't be updated

mysql> update film_text set title = 'Test' where film_id = 1001;

ERROR 1099 (HY000): Table 'film_text' was locked with a READ lock and can't be updated

其他session更新鎖定表會等待獲得鎖:

mysql> update film_text set title = 'Test' where film_id = 1001;

等待

釋放鎖

mysql> unlock tables;

Query OK, 0 rows affected (0.00 sec)

等待

Session獲得鎖,更新操作完成:

mysql> update film_text set title = 'Test' where film_id = 1001;

Query OK, 1 row affected (1 min 0.71 sec)

Rows matched: 1 Changed: 1 Warnings: 0

當使用LOCK TABLES時,不僅需要一次鎖定用到的所有表,而且,同一個表在SQL語句中出現多少次,就要透過與SQL語句中相同的別名鎖定多少次,否則也會出錯!舉例說明如下。

(1)對actor表獲得讀鎖:

mysql> lock table actor read;

Query OK, 0 rows affected (0.00 sec)

(2)但是透過別名訪問會提示錯誤:

mysql> select a.first_name,a.last_name,b.first_name,b.last_name from actor a,actor b where a.first_name = b.first_name and a.first_name = 'Lisa' and a.last_name = 'Tom' and a.last_name <> b.last_name;

ERROR 1100 (HY000): Table 'a' was not locked with LOCK TABLES

(3)需要對別名分別鎖定:

mysql> lock table actor as a read,actor as b read;

Query OK, 0 rows affected (0.00 sec)

(4)按照別名的查詢可以正確執行:

mysql> select a.first_name,a.last_name,b.first_name,b.last_name from actor a,actor b where a.first_name = b.first_name and a.first_name = 'Lisa' and a.last_name = 'Tom' and a.last_name <> b.last_name;

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

| first_name | last_name | first_name | last_name |

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

| Lisa       | Tom       | LISA       | MONROE    |

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

1 row in set (0.00 sec)

WRITE鎖表

獲得表film_text的WRITE鎖定

mysql> lock table film_text write;

Query OK, 0 rows affected (0.00 sec)

當前session對鎖定表的查詢、更新、插入操作都可以執行:

mysql> select film_id,title from film_text where film_id = 1001;

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

| film_id | title |

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

| 1001 | Update Test |

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

1 row in set (0.00 sec)

mysql> insert into film_text (film_id,title) values(1003,'Test');

Query OK, 1 row affected (0.00 sec)

mysql> update film_text set title = 'Test' where film_id = 1001;

Query OK, 1 row affected (0.00 sec)

Rows matched: 1 Changed: 1 Warnings: 0

其他session對鎖定表的查詢被阻塞,需要等待鎖被釋放:

mysql> select film_id,title from film_text where film_id = 1001;

等待

釋放鎖:

併發插入(ConcurrentInserts)

上文提到過MyISAM表的讀和寫是序列的,但這是就總體而言的。在一定條件下,MyISAM表也支援查詢和插入操作的併發進行。

MyISAM儲存引擎有一個系統變數concurrent_insert,專門用以控制其併發插入的行為,其值分別可以為0、1或2。

l當concurrent_insert設定為0時,不允許併發插入。

l當concurrent_insert設定為1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個程式讀表的同時,另一個程式從表尾插入記錄。這也是MySQL的預設設定。

l當concurrent_insert設定為2時,無論MyISAM表中有沒有空洞,都允許在表尾併發插入記錄。

在如表20-4所示的例子中,session_1獲得了一個表的READLOCAL鎖,該執行緒可以對錶進行查詢操作,但不能對錶進行更新操作;其他的執行緒(session_2),雖然不能對錶進行刪除和更新操作,但卻可以對該表進行併發插入操作,這裡假設該表中間不存在空洞。

表20-4MyISAM儲存引擎的讀寫(INSERT)併發例子

session_1

session_2

獲得表film_text的READ LOCAL鎖定

mysql> lock table film_text read local;

Query OK, 0 rows affected (0.00 sec)

當前session不能對鎖定表進行更新或者插入操作:

mysql> insert into film_text (film_id,title) values(1002,'Test');

ERROR 1099 (HY000): Table 'film_text' was locked with a READ lock and can't be updated

mysql> update film_text set title = 'Test' where film_id = 1001;

ERROR 1099 (HY000): Table 'film_text' was locked with a READ lock and can't be updated

其他session可以進行插入操作,但是更新會等待:

mysql> insert into film_text (film_id,title) values(1002,'Test');

Query OK, 1 row affected (0.00 sec)

mysql> update film_text set title = 'Update Test' where film_id = 1001;

等待

當前session不能訪問其他session插入的記錄:

mysql> select film_id,title from film_text where film_id = 1002;

Empty set (0.00 sec)

釋放鎖:

mysql> unlock tables;

Query OK, 0 rows affected (0.00 sec)

等待

當前session解鎖後可以獲得其他session插入的記錄:

mysql> select film_id,title from film_text where film_id = 1002;

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

| film_id | title |

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

| 1002 | Test |

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

1 row in set (0.00 sec)

Session2獲得鎖,更新操作完成:

mysql> update film_text set title = 'Update Test' where film_id = 1001;

Query OK, 1 row affected (1 min 17.75 sec)

Rows matched: 1 Changed: 1 Warnings: 0

可以利用MyISAM儲存引擎的併發插入特性,來解決應用中對同一表查詢和插入的鎖爭用。例如,將concurrent_insert系統變數設為2,總是允許併發插入;同時,透過定期在系統空閒時段執行OPTIMIZETABLE語句來整理空間碎片,收回因刪除記錄而產生的中間空洞

MyISAM的鎖排程

MyISAM儲存引擎的讀鎖和寫鎖是互斥的,讀寫操作是序列的。那麼,一個程式請求某個MyISAM表的讀鎖,同時另一個程式也請求同一表的寫鎖,MySQL如何處理呢?答案是寫程式先獲得鎖。不僅如此,即使讀請求先到鎖等待佇列,寫請求後到,寫鎖也會插到讀鎖請求之前!這是因為MySQL認為寫請求一般比讀請求要重要。這也正是MyISAM表不太適合於有大量更新操作和查詢操作應用的原因,因為,大量的更新操作會造成查詢操作很難獲得讀鎖,從而可能永遠阻塞。這種情況有時可能會變得非常糟糕!幸好我們可以透過一些設定來調節MyISAM的排程行為。

透過指定啟動引數low-priority-updates,使MyISAM引擎預設給予讀請求以優先的權利。

透過執行命令SETLOW_PRIORITY_UPDATES=1,使該連線發出的更新請求優先順序降低。

透過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,降低該語句的優先順序。

雖然上面3種方法都是要麼更新優先,要麼查詢優先的方法,但還是可以用其來解決查詢相對重要的應用(如使用者登入系統)中,讀鎖等待嚴重的問題。

另外,MySQL也提供了一種折中的辦法來調節讀寫衝突,即給系統引數max_write_lock_count設定一個合適的值,當一個表的讀鎖達到這個值後,MySQL就暫時將寫請求的優先順序降低,給讀程式一定獲得鎖的機會。

上面已經討論了寫優先排程機制帶來的問題和解決辦法。這裡還要強調一點:一些需要長時間執行的查詢操作,也會使寫程式“餓死”!因此,應用中應儘量避免出現長時間執行的查詢操作,不要總想用一條SELECT語句來解決問題,因為這種看似巧妙的SQL語句,往往比較複雜,執行時間較長,在可能的情況下可以透過使用中間表等措施對SQL語句做一定的“分解”,使每一步查詢都能在較短時間完成,從而減少鎖衝突。如果複雜查詢不可避免,應儘量安排在資料庫空閒時段執行,比如一些定期統計可以安排在夜間執行。

©著作權歸作者所有:來自51CTO部落格作者陳仲陽0的原創作品,如需轉載,請註明出處,否則將追究法律責任

mysql資料庫


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/758/viewspace-2820719/,如需轉載,請註明出處,否則將追究法律責任。

相關文章