目錄node
表就是關於特定實體的數據集合,是關係型數據庫模型的核心。mysql
在INNODB存儲引擎中,表都是根據主鍵順序組織存放的。這種存儲方式的表稱爲索引組織表。在INNODB存儲引擎表中,每張表都有個主鍵,若是在建立表時沒有顯式地定義主鍵,則INNODB存儲引擎會按以下方式選擇或建立主鍵。算法
首先判斷表中是否有非空的惟一索引,若是有,則該列爲主鍵。sql
表中有多個非空惟一索引時候,引擎選擇建表時第一個定義的非空惟一索引做爲主鍵。數據庫
這個的主鍵是d
。安全
對於主鍵只有一個的表,可使用_rowid
查詢主鍵值架構
SELECT a,b,c,d,_rowid FROM z;
全部數據都放在表空間中。表空間又由段、區、頁組成。less
默認狀況下,INNODB存儲引擎有一個共享表空間
ibdata1`,即全部數據存放在這個默認表空間。函數
經過參數innodb_file_per_table
能夠將每張表內的數據能夠單獨放到一個表空間內。可是單獨表空間內存放的只是數據、索引和插入緩衝Bitmap
頁,其餘類的數據,如回滾信息,插入緩衝索引頁,系統事務信息,二次寫緩衝等仍是放在原來的默認空間。性能
常見的段有:數據段、索引段、回滾段等。
數據段是B+樹的葉子節點,索引段是B+樹的非葉子節點。
區是由連續頁組成的空間,在任何狀況下的大小都爲1MB。可是頁的大小可能不一樣。
可是有個問題???新建立的表默認大小是96K,區中是64個連續的頁,建立表至少是1MB啊。其實由於在每一個段開始時,先用32個頁大小的碎片也來存放數據,在使用完這些頁以後纔是64個連續頁的申請。目的是,對於一些小表或者undo這類的段,能夠在開始時申請較少的空間,節省磁盤容量的開銷。
是INNODB磁盤管理的最小單位。能夠經過innodb_page_size
將頁的大小設置爲4K、8K、16K。若設置完成,則全部表中頁的大小都爲innodb_page_size
,不能夠對其再次進行修改。除非經過mysqldump
導入和導出操做來產生新的庫。
常見頁的類型:
B-tree Node
undo Log Page
System Page
Transaction system Page
Insert Buffer Bitmap
Insert Buffer Free List
Uncompressed BLOB Page
compressed BLOB Page
INNODB存儲引擎是面向列的,也就是說數據是按行進行存放的
記錄數據的行格式有兩種:
# 經過這個,能夠查看錶使用的行格式,row_format表示行格式。 SHOW TABLE STATUS LIKE 'table_name';
首部,是一個非NULL變長字段長度列表,而且其是按照列的順序逆序放置的,其長度爲:
列的長度小於255字節,用1字節表示
大於255個字節,用2字節表示
不可超過2字節,由於MYSQL數據庫中VARCHAR類型的最大長度爲65535.
NULL標誌位「1」表示該行數據中有NULL值。
記錄頭信息,固定佔用5字節(40位)
最後的部分是實際存儲每一個列的數據,NULL不佔該部分的任何空間。還有兩個隱藏列,事務ID列和回滾指針列。若沒有主鍵,還會增長一個6字節的rowid列。
使用命令能夠查看頁內容
hexdump -C -v mytest.ibd > mytest.txt
首部是一個字段長度偏移列表,按照逆序排放。
記錄頭信息
最後就是列數據了。
INNODB
存儲引擎能夠將一條記錄中的某些數據存儲在真正的數據頁面以外。
研究下VARCHAR數據類型的行溢出行爲~
首先VARCHAR的長度官方說明爲65535,但實際上建立表會報錯,經測試,最大長度爲65532。可是實際上數據庫已經將VARCHAR轉換爲了TEST
可是注意使用的字符類型是latin1,若是改成GBK或者utf-8呢?
也會報錯,說明VARCHAR(N)中的N是指字符的長度。且是整個表全部的列的超度綜合不能超過N。
可是即便可以存放65532個字節,可是一個頁只有16KB(16384字節),所以,在通常狀況下,INNDB存儲引擎的數據都是存放在頁類型爲B-tree node中。可是發生行溢出時,數據存放在頁類型爲Uncompress BLOB
頁中。對於行移除數據採用存放方式如圖:
INNODB存儲引擎表是索引組織的,即B+Tree機制,因此每一個頁至少要有兩條行記錄(不然回退成列表),這個閾值通過測試後是8098。
類比TEXT或者BLOB的數據類型,跟VARCHAR一致
,8098長度以後的才存放在Uncompressed BLOB Page
中,不然仍是存放在數據頁中。
Compressed
和Dynamic
行記錄格式 原有的Compact
和Redundant
統稱爲Antelope
文件格式
新的文件格式Compressed
和Dynamic
統稱爲Barracuda
新的兩種記錄格式對於存放子BLOB中的數據採用了徹底的行溢出的方式
而Compressed
的另外一個功能就是,存儲在其中的行數據會以zlib
的算法進行壓縮。
對於多機子字符編碼的CHAR數據類型的存儲,INNODB存儲引擎在內部視爲VARCHAR實現。
由如下七個部分組成:
FIle Header
文件頭Page Header
頁頭Infimun
和 Supremum Records
User Records
用戶記錄,即行記錄Free Space
空閒空間Page Directory
頁目錄File Tralier
文件結尾信息 關係數據庫自己能保證存儲數據的完整性。使用約束機制來保障完整性。
INNODB保障實體完整性:
Primary Key
或Unique Key
約束來保障實體的完整性。 域完整性保證數據每列的值知足特定的條件。經過一下途徑來保障:
參照完整性保證兩張表之間的關係。經過外鍵來保障。也能夠經過觸發器來強制執行。
對於INNODB存儲引擎自己而言,提供瞭如下幾種約束:
Primary Key
Unique Key
Foreign Key
Default
NOT NULL
建立方式:
建立主鍵和惟一索引,主鍵約束名爲PRIMARY,惟一索引約束名爲列名。
CREATE TABLE u ( id int, name varchar(20), id_card char(18), PRIMIARY KEY(id), UNIQUE KEY (name) ); # 查詢約束 select constarint_name, constraint_type from information_schema.TABLE_CONSTRAINTS where table_schema='mytest' AND table_name='u'\G;
可是使用ALTER TABLE的話能夠修改惟一索引的約束名。
# uk_id_card爲約束名 id_card爲列名 ALTER TABLE u ADD UNIQUE KEY uk_id_card (id_card);
建立Foreign Key
約束
CREATE TABLE p( id int, u_id int, primary key (id), foreign key (u_id) references p (id) );
還能夠經過查看錶REFERENTIAL_CONSTRAINTS,而且能夠詳細地瞭解外鍵的屬性。
SELECT * FROM information_schema.REFERNTIAL_CONSTRAINTS WHRER constraint_schema='mytest'\G;
在某些默認設置下,MYSQL數據庫容許非法的或者不正確的數據的插入或更新,又或者能夠在數據庫內部轉換爲一個合法的值,例如像對NOT NULL的字段插入一個NULL值,MYSQL數據庫會將其改成0再進行插入,所以數據庫自己沒有對數據的正確性進行約束。
可是上述狀況MYSQL會發出警告,可是想設置爲報錯,必須設置參數sql_mode
來嚴格審覈輸入的參數。
SET sql_mode = 'STRICT_TRANS_TABLES';
MYSQL數據庫不支持傳統的CHECK約束,可是經過ENUM和SET類型能夠解決部分這樣的約束需求
依舊能夠設置參數sql_mode
來嚴格要求
建立觸發器的語法
CREATE [DEFINER = {user | CURRENT_USER}] TRIGGER trigger_name BEFORE|AFTER INSERT|UPDATE|DELETE ON tb1_name FOR EACH ROW trigger_stmt
一個表最多建立6個觸發器,也僅支持按每行記錄進行觸發。
能夠經過建立觸發器實現約束的一種手段和方法。
CREATE TABLE parent( id INT NOT NULL PRIMARY KEY(id) )ENGINE = INNODB; CREATE TABLE child( id INT, parent_id INT, FOREIGN KEY(parent_id) REFERENCES parent(id) ON DELETE RESTRICT )ENGINE = INNODB
被引用的表爲父表,引用的表稱爲子表。外鍵定義時的ON DELETE 和 ON UPDATE表示在對父表進行DELETE 和 UPDATE操做時,對子表作的操做,能夠定義的子表操做有:
在MYSQL中,視圖是一個命名的虛表,它由一個SQL查詢定義,能夠當作表使用。與持久表不一樣的是,視圖中的數據沒有實際的物理存儲。
語法:
CREATE VIEW v_t AS SELECT * FROM t WHERE id < 0;
被用做一個抽象裝置,也能夠起到一個安全層的做用。
分區的過程是將一個表或索引分解爲多個更小、更可管理的部分。就訪問數據庫的應用而言,從邏輯上只有一個表或者索引,可是在物理上這個表或者索引可能由數十個物理分區組成。沒個分區都是獨立的對象,能夠獨自處理,也能夠做爲一個更大對象的一部分進行處理。
MYSQL數據庫支持的分區類型爲水平分區,即將同一張表中不一樣行的記錄分配到不一樣的物理文件中。MYSQL的分區都是局部分區索引,一個分區中既存放了數據又存放了索引。而全局分區是指,數據存放在各個分區中,可是全部數據的索引放在一個對象中。
查看數據庫是否啓動了分區功能
SHOW VARIABLES LIKE '%partition%'\G; SHOW PLUGINS\G;
使用分區,並不必定會是使得數據運行的更快。分區可能會給某些SQL語句性能提升,可是主要用於數據庫高可用性的管理。
MYSQL支持的分區
RANGE分區:行數據基於屬於一個給定連續區間的列值被放入分區。
LIST分區:LIST分區面向的是離散的值
HASH分區:根據用戶自定義表達式返回值來進行分區
KEY分區:根據MYSQL提供的哈希函數來進行分區。
不論使用何種分區,表中存在在主鍵或者惟一索引時,分區列必須是惟一索引的一個組成部分。
RANGE分區
# id小於10 數據插入到p0分區, id大於等於10小於20,輸入插入到p1分區 CREATE TABLE t ( id INT )ENGINE=INNODB PARTITION BY RANGE(id)( PARTITION p0 VALUES LESS THAN(10), PARTITION p1 VALUES LESS THAN(20) );
分區以後,表再也不由一個ibd文件組成了,而是由多個分區Ibd文件組成。
能夠經過查詢infomation_scheme
架構下的PARTITIONS
表來查看每一個分區的具體信息
SELECT * FROM information_scheme.PARTITIONS WHERE table_schema=database() AND table_name='t'\G;
若是咱們插入id爲30的數值,會拋出異常,不讓添加。因此咱們須要再添加一個MAXVALUE的值的分區。
ALTER TABLE t ADD PARTITION( partition p2 values less than maxvalue); )
RANGE主要用於日期列的分區,一個demo
CREATE TABLE sales( money INT UNSIGNED NOT NULL date DATETIME )ENGINE = INNODB PARTITION by RANGE(YEAR(date)) ( PARTITION p2008 VALUES LESS THAN (2009), PARTITION p2009 VALUES LESS THAN (2010), PARTITION p2010 VALUES LESS THAN (2011) );
這樣建立的好處是,管理sales這樣表,若是要刪除2008年的數據,不用執行SQL,只需刪除2008年數據所在的分區便可。查詢2008年的數據也會變快。
對於sales這張分區表,設計按照每一年每個月進行分區
CREATE TABLE sales( money INT UNSIGNED NOT NULL, date DATETIME )ENGINE = INNODB PARTITION by RANGE(YEAR(date)*100+MONTH(date)) ( PARTITION p201001 VALUES LESS THAN (201001), PARTITION p201002 VALUES LESS THAN (201002), PARTITION p201003 VALUES LESS THAN (201003) );
可是執行先SQL的時候仍是去查找了3個分區
EXPLAIN PARTITIONS SELECT * FROM sales WHERE date>='2010-01-01' AND date<='2010-01-31'\G;
這個是因爲對RANGE分區的查詢,優化器只能對YEAR()
,TO_DAYS()
,TO_SECONDS()
,UNIX_TIMESTAMP()
這類函數進行優化選擇。所以對於上述需求,應該更改成:
CREATE TABLE sales( money INT UNSIGNED NOT NULL, date DATETIME )ENGINE = INNODB PARTITION by RANGE(TO_DAYS(date)) ( PARTITION p201001 VALUES LESS THAN (TO_DAYS('2010-02-01')), PARTITION p201002 VALUES LESS THAN (TO_DAYS('2010-03-01')), PARTITION p201003 VALUES LESS THAN (TO_DAYS('2010-04-01')) );
LIST分區
與RANGE分區很類似,只是分區列的值是離散的,而非連續的。
CREATE TABLE t ( a INT, b INT )ENGINE = INNODB PARTITION BY LIST(b) ( PARTITION P0 VALUES IN (1,3,5,7,9), PARTITION P1 VALUES IN (0,2,4,6,8) );
HASH分區
目的是將數據均勻地分佈到預先定義的各個分區中,保證各分區的數據數量大體同樣的。
用戶不須要指定列值,只要基於將要進行哈希分區的列值指定一個列值或者表達式,以及指定分區的表要被分割成的分區數量。
CREATE TABLE t_hash( a INT, b DATETIME )ENGING=INNODB # expr返回一個整數的表達式。僅僅是字段類型爲MYSQL整形的列名 PARTITION BY HASH (YEAR(b)) # 劃分分區的數量 PARTITIONS 4
其實就是使用取餘的方式分配到不一樣的分區中。mod(YEAR(b), 4)
還支持一種LINEAR HASH的分區,語法與HASH一致。可是進行分區的判斷算法不一樣
取大於分區數量的下一個2的冪值V,V=POWER(2, CEILING(LOG(2, num)))
所在分區N=YEAR('2010-04-01')&(V-1)
優點在於增長、刪除、合併和拆分分區變得更加快捷,這有利於處理含有大量數據的表。缺點是,數據分佈可能不是太均衡。
KEY分區
和HASH分區類似,不一樣之處HASH使用用戶定義的函數進行分區,KEY分區使用MYSQL數據庫提供的函數進行分區,INNODB使用哈希函數。
CREATE TABLE t_hash( a INT, b DATETIME )ENGING=INNODB PARTITION BY KEY (b) PARTITIONS 4
COLUMNS分區
能夠對多個列的值進行分區,能夠支持的數據類型爲:
全部整型類型,浮點型不支持
日期類型:DATE和DATETIME
字符串類型:CHAR 、VARCHAR、BINARY、VARBINARY,不支持BLOB和TEXT
能夠用來替代RANGE 和LIST分區
CREATE TABLE t_hash( a INT, b DATETIME )ENGING=INNODB PARTITION BY RANGE COLUMNS(b)( partition p0 values less than ('2009-01-01'), partition p1 values less than ('201--01-01'), ) PARTITIONS 4
子分區是在分區的基礎上再進行分區,有時也稱這種分區爲複合分區。MYSQL容許數據庫在RANGE和LIST的分區上再進行HASH或KEY的子分區。
CREATE TABLE ts(a INT, b DATE) PARTITION BY RANGE(YEAR(b)) SUBPARTITION BY HASH(TO_DAYS(b)) ( PARTITION p0 VALUES LESS THAN (1990) ( SUBPARTITION S0, SUBPARTITION s1 ) PARTITION p1 VALUES LESS THAN (2000) ( SUBPARTITION S2, SUBPARTITION s3 ) PARTITION p2 VALUES LESS THAN MAXVALUE ( SUBPARTITION S4, SUBPARTITION s5 ) );
注意點:
SUBPARTITION
來明肯定義任何子分區,就必須定義全部的子分區。SUBPARTITION
子句必須包括子分區的一個名字。 MYSQL容許對NULL值作分區,在MYSQL中,NULL值視爲小於任何一個非NULL值。
對於RANGE分區,若是插入了NULL值,會放在最左邊的分區。
對於LIST分區,若是要是用NULL值,必須顯式地指出哪一個分區中放入NULL值,不然會報錯。、
CREATE TABLE t_list( a INT, b INT )ENGINE=INNODB PARTITION BY LIST(b)( PARTITION p0 VALUES IN (1,3,5,7,9, NULL), PARTITION p1 VALUES IN (0,2,4,6,8) )
對於HASH和KEY分區,任何分區函數都會將含有NULL值的記錄返回爲0.
分區不必定會提高查詢速度。
數據庫的應用分爲兩類:
OLTP
OLAP
對於OLAP應用,分區能夠提高查詢性能。由於OLAP大多數查詢須要頻繁掃描一張很大的表。假設有一個一億行的表,其中有時間戳屬性,用戶的查詢須要從這張表中獲取一年的數據。若是按時間戳進行分區。則只須要掃響應分區便可。
對於OLTP應用,一般不會獲取一張大表中10%的數據,大部分是經過索引返回幾條記錄便可。而根據B+樹索引的原理可知,對於一張大表,通常的B+樹須要2~3次的磁盤IO。所以B+樹能夠很好的完成操做,不須要分區的幫助。
ALTER TABLE ... EXCHANGE PARTITION
可讓分區或子分區中的數據與另一個非分區的表中的數據進行交換。
ALTER TABLE e EXCHANGE PARTITION p0 WITH TABLE e2;