《MySQL技術內幕:InnoDB存儲引擎》本書從源代碼的角度深度解析了InnoDB的體系結構、實現原理、工做機制,並給出了大量實踐,本着將書讀薄的思想,按部就班的記錄對本書學習的讀書筆記。
關係型數據庫系統和文件系統的一個不一樣點是,關係數據庫自己能保證存儲數據的完整性,不須要應用程序的控制,而文件系統通常須要在程序端進行控制。當前幾乎全部的關係型數據庫都提供了約束(constraits)機制,該機制提供了一條強大而簡易的途徑來保證數據庫中的數據完整性,通常來講,數據完整性有如下三種形式:mysql
(1)實體完整性保證表中有一個主鍵,在InnoDB存儲引擎中,用戶能夠經過定義Primary Key或Unique Key約束來保證明體的完整性,用戶還能夠編寫一個觸發器來保證數據完整性、sql
(2)域完整性保證數據每列的值知足特定的條件。在InnoDB存儲引擎中,域完整性能夠經過如下途徑來保證:數據庫
a. 選擇適合的數據類型確保一個數據值知足條件架構
b. 外鍵(Foreign Key)約束學習
c. 編寫觸發器spa
d. 還能夠考慮用default約束做爲強制域完整性的一個方面code
(3)參照完整性保證兩張表之間的關係,InnoDB存儲引擎提供瞭如下幾種約束:orm
a. primary keyblog
b. unique key索引
c. foreign key
d. default
e. not null
約束的建立有如下兩種方式:
(1)表創建時就進行約束定義
(2)利用alter table命令來進行建立約束
如下幾點須要關注和注意:
a. 對Unique Key(惟一索引)的約束,用戶除了在建立時約定,還能夠經過Create Unique Index來建立
b. 對於主鍵約束耳音,其默認約束名爲PRIMARY,而對於Unique Key約束而言,默認約束名和列名同樣,固然也能夠人爲的指定Unique Key的名字,Foreign Key約束彷佛會有一個比較神祕的默認名稱
mysql> Create table u( -> id int, -> id_card varchar(18), -> name varchar(20), -> primary key(id), -> unique key(name) -> )engine=InnoDB; Query OK, 0 rows affected (0.56 sec) mysql> select constraint_name,constraint_type -> from -> information_schema.table_constraints -> where table_schema='test' and table_name='u'; +-----------------+-----------------+ | constraint_name | constraint_type | +-----------------+-----------------+ | PRIMARY | PRIMARY KEY | | name | UNIQUE | +-----------------+-----------------+ 2 rows in set (0.01 sec)
能夠看到,約束名如上面所說,主鍵的約束名爲PRIMARY,惟一索引的默認約束名與列名相同。
mysql> alter table u -> add unique key uk_id(id_card); Query OK, 0 rows affected (0.19 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> select constraint_name,constraint_type -> from -> information_schema.table_constraints -> where table_schema='test' and table_name='u'; +-----------------+-----------------+ | constraint_name | constraint_type | +-----------------+-----------------+ | PRIMARY | PRIMARY KEY | | name | UNIQUE | | uk_id | UNIQUE | +-----------------+-----------------+ 3 rows in set (0.00 sec)
mysql> create table p( -> id int, -> u_id int, -> primary key(id), -> foreign key(u_id) references p(id) -> )engine=InnoDB; Query OK, 0 rows affected (0.41 sec) mysql> select constraint_name,constraint_type -> from -> information_schema.table_constraints -> where table_schema='test' and table_name='p'; +-----------------+-----------------+ | constraint_name | constraint_type | +-----------------+-----------------+ | PRIMARY | PRIMARY KEY | | p_ibfk_1 | FOREIGN KEY | +-----------------+-----------------+ 2 rows in set (0.00 sec)
在上面的例子中,經過information_schema架構下的表table_constraints來查看當前MySql庫下全部的約束信息。
在某些默認設置下,MySql數據庫容許非法或不正確的數據的插入或更新,又或者能夠在數據庫內部將其轉化爲一個合法的值,如向not null的字段插入一個null值,MySql數據庫會將其更改成0再進行插入,所以數據庫自己沒有對數據的正確性進行約束。
mysql> Create table a( -> id int not null, -> data date not null -> )engine=InnoDB; Query OK, 0 rows affected (0.20 sec) mysql> insert into a select NULL, '2009-02-20'; Query OK, 1 row affected, 1 warning (0.05 sec) Records: 1 Duplicates: 0 Warnings: 1
mysql> select * from a; +----+------------+ | id | data | +----+------------+ | 0 | 2009-02-20 | +----+------------+ 1 row in set (0.00 sec)
經過設置參數set sql_mode='strict_trans_tables';對MySql數據庫的輸入值進行了約束,並且針對不一樣的錯誤提示錯誤內容也不一樣。
mysql> Create table a( -> id int, -> sex enum('male','female') -> )engine=InnoDB; Query OK, 0 rows affected (0.17 sec) mysql> insert into a select 1,'male'; Query OK, 1 row affected (0.06 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> insert into a select 2,'hello'; ERROR 1265 (01000): Data truncated for column 'sex' at row 1
觸發器的做用是在執行insert,delete和update命令以前或以後自動調用sql命令或存儲過程
Create [definer = { user | current_user}] trigge trigger_name BEFORE|AFTER INSERT|UPDATE|DELETE on tble_name FOR EACH ROW trigger_stmt
(1)最多能夠爲一個表創建6個觸發器,即分別爲insert,update,delete的before和after各定義一個
(2)只有表才支持觸發器,視圖不支持(臨時表也不支持)
(3)若是before觸發器失敗,則MySQl將不執行請求的操做,此外若是before觸發器或語句自己失敗,MySql將不執行after觸發器(若是有的話)
DROP TRIGGER trigger_name;
注:觸發器不能更新或覆蓋,爲了修改一個觸發器,必須先刪除它,而後再從新建立
假設有張用戶消費表,每次用戶購買同樣物品後其金額都是減的,若這時有不壞好意的用戶作了一個相似減去一個負值的操做,這樣用戶的錢沒有減小反而不斷增長
mysql> Create table usercash( -> userid int not null, -> cash int unsigned not null -> , -> Primary key(userid)) -> engine=InnoDB; Query OK, 0 rows affected (0.16 sec) mysql> insert into usercash select 1,1000; Query OK, 1 row affected (0.03 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> update usercash set cash=cash-(-20) where userid = 1; Query OK, 1 row affected (0.05 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from usercash; +--------+------+ | userid | cash | +--------+------+ | 1 | 1020 | +--------+------+ 1 row in set (0.00 sec)
上面運行的SQL語句對數據庫來講沒有任何的問題,均可以正常運行,不會報錯。但從業務邏輯上來講,這是絕對錯誤的。下面採用觸發器來約束這個邏輯行爲:
mysql> Create table usercash_error_log( -> userid int not null, -> old_cash int unsigned not null, -> new_cash int unsigned not null, -> user varchar(30), -> time datetime, -> primary key(userid) -> )engine=InnoDB; Query OK, 0 rows affected (0.23 sec) mysql> delimiter // mysql> Create trigger tgr_usercash_update before update on usercash -> for each row -> begin -> if new.cash -old.cash>0 then -> insert into usercash_error_log select old.userid, old.cash,new.cash,US ),NOW(); -> set new.cash=old.cash; -> end if; -> end// Query OK, 0 rows affected (0.05 sec) mysql> delimiter ; mysql> delete from usercash; Query OK, 1 row affected (0.05 sec) mysql> insert into usercash select 1,1000; Query OK, 1 row affected (0.05 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> update usercash set cash=cash-(-20) where userid=1; Query OK, 0 rows affected (0.11 sec) Rows matched: 1 Changed: 0 Warnings: 0 mysql> select * from usercash; +--------+------+ | userid | cash | +--------+------+ | 1 | 1000 | +--------+------+ 1 row in set (0.00 sec) mysql> select * from usercash_error_log; +--------+----------+----------+----------------+---------------------+ | userid | old_cash | new_cash | user | time | +--------+----------+----------+----------------+---------------------+ | 1 | 1000 | 1020 | root@localhost | 2018-06-01 15:09:51 | +--------+----------+----------+----------------+---------------------+ 1 row in set (0.00 sec)
能夠看出此次對於異常的數據更新經過觸發器將其保存到了usercash_error_log。此外該觸發器還記錄了操做該SQL語句的用戶和時間。
外鍵用來保證參照完整性,MySQL數據庫的MyIsAM存儲引擎自己並不支持外鍵,對於外鍵的定義只是起到一個註釋的做用,而InonoDB存儲引擎則完整支持外鍵約束。
通常來講,稱被引用的表爲父表,引用的表稱爲子表,外鍵定義時的on delete和on update表示在對父表進行delete和updata操做時,對子表所作的操做。可定義的子表操做有:
(1)CASCADE
表示當父表發生delete和update操做時,對相應的子表中的數據頁進行delete和update操做
(2)SET FULL
表示當父表發生delete和update操做時,相應的子表中的數據被更新爲NULL值,可是子表中對應的列必須容許爲NULL值
(3)NO ACTION
表示父表發生delete或update操做時,拋出錯誤,不容許這類操做發生
(4)RESTRICT
表示父表發生delete或update操做時,拋出錯誤,不容許這類操做發生,若是定義外鍵時沒有指定on delete或on update,RESTRICT就是默認的外鍵設置