mysql存儲引擎MyISAM
1,創建myisam表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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
當使用這個參數的時候,這個列一定要是主鍵
1 2 3 4 5 6 7 8 9 10 11 12 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 的值,但是這個值設置了的話,就會從這個值開始累積
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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
1 2 3 4 5 6 7 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已經沒有用了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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
1 2 3 4 5 6 7 mysql> show?variables like?"%pointer%"; +--------------------------+-------+ | Variable_name??????????? | Value | +--------------------------+-------+ | myisam_data_pointer_size |?6?????| +--------------------------+-------+ 1?row?in?set?(0.00?sec)
我們來做個實驗試試
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 如果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狀態變量來分析系統上的表鎖定爭奪:
1 2 3 4 5 6 7 8 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鎖表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 獲得表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鎖表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 獲得表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)并發例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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語句做一定的“分解”,使每一步查詢都能在較短時間完成,從而減少鎖沖突。如果復雜查詢不可避免,應盡量安排在數據庫空閑時段執行,比如一些定期統計可以安排在夜間執行。
本文出自 “ 好好活著” 博客,請務必保留此出處 http://wolfword.blog.51cto.com/4892126/1295168
總結
以上是生活随笔 為你收集整理的mysql dba系统学习(20)mysql存储引擎MyISAM 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。