目录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;