表的外键约束引入表格中的一列或两列,結果务必可以标志表格中每排纪录的唯一性。InnoDB表有数据库索引机构表,外键约束既是数据信息也是数据库索引。

外键约束的设计原理。

1.空中间的间隔应当不大。

在前一篇文章中,大家详细介绍了InnoDB外键约束的数据存储方式。外键约束占有的室内空间越小空,每一个数据库索引页中储存的键值就越大,因而一次能够放进运行内存的数据信息就越大。

2.最好是有一定的排列特性。

假如应用INT32种类做为外键约束,而且值是严苛排列的,那麼必须在原始记录页后边提升一条新纪录,或是在数据信息页后边提升一个空页来添充纪录,才可以插进新纪录,那样严苛排列的外键约束的载入速率会十分快。

3.基本数据类型是塑胶的。

基本数据类型早已探讨过去了。依据前二点的规定,最理想化的是挑选整数金额种类,例如int32无标记。次序数据信息提高要不由数据库查询自身转化成,要不由业务流程自动生成。

1.为与业务流程不相干的特性建立外键约束。

1.1填加字段名做为外键约束。

这也是MySQL最强烈推荐的方法。一般INT32能够达到绝大多数情景,单独数据库查询和单独表较多能够储存42亿行纪录。新加上的含有自加上字段名的纪录将被次序加上到当今数据库索引连接点的事后部位,直至数据信息页已满,随后将载入新页。这将大大减少数据信息页的任意IO。

应用自加上字段名做为外键约束时,很有可能必须留意2个难题:

第一个难题:MySQL原生态自增密匙分拆。

假如中后期数据信息提高,预估拆卸数据库查询和表,能够考虑到应用INT64;MySQL原生态适用数据库查询和表拆卸的自增外键约束,由自增步幅和起始值决策。MySQL连接点最少要有2个,每一个连接点的自增加量步幅为2。假定server_id各自为1和2,自增起始值还可以为1和2。假定下边是第一个MySQL连接点。设定步幅和起始值后,表tmp插进三行,每一行都严苛依照set方式插进数据信息。

mysql>set@@auto_increment_increment=2;QueryOK,0rowsaffected(0.00sec)mysql>set@@auto_increment_offset=1;QueryOK,0rowsaffected(0.00sec)mysql>insertintotmpvalues(null),(null),(null);QueryOK,3rowsaffected(0.01sec)Records:3Duplicates:0Warnings:0mysql>select*fromtmp; ---- |id| ---- |1||3||5| ---- 3rowsinset(0.00sec)

殊不知,这一块MySQL不可以确保别的值不容易矛盾。比如,假如读取连接点2的值,还可以取得成功插进。默认设置状况下,MySQL对这一块沒有限定。数据信息进库前最好是检查一下。

mysql>insertintotmpvalues(2);QueryOK,1rowaffected(0.02sec)mysql>select*fromtmp; ---- |id| ---- |1||2||3||5| ---- 4rowsinset(0.00sec)

第二个难题:MySQL自加上密匙合拼。

这个问题一般涉及到旧平台的更新改造和升級。例如好几个各分部的旧平台的数据信息必须合拼到新系统中,以前不可以简易合拼每一个各分部的自加外键约束,很有可能会发生外键约束矛盾。例如假定武汉市每一个区都是有自身的医疗保险数据信息,之前每一个区都是有自身单独设计方案的数据库查询。如今医疗保险要升級到全省统一,要设定一个新的以大城市为公司的数据库查询实体模型。

武昌区数据信息如下所示,相匹配表n1。

mysql>select*fromn1; ---- |id| ---- |1||2||3| ---- 3rowsinset(0.00sec)

光谷的数据信息如下所示,相匹配表n2。

mysql>select*fromn2; ---- |id| ---- |1||2||3| ---- 3rowsinset(0.00sec)

由于前2个区的数据库查询设计师沒有考虑到之后合拼,因此每一个区的表都是有自身单独的自增外键约束。

那样,考虑到创建一个含有新的自加上id的明细表n3,设计方案并导进旧平台的ID。

mysql>createtablen3(idintauto_incrementprimarykey,old_idint);QueryOK,0rowsaffected(0.07sec)mysql>insertinton3(old_id)select*fromn1unionallselect*fromn2;QueryOK,6rowsaffected(0.01sec)Records:6Duplicates:0Warnings:0mysql>select*fromn3; ---- -------- |id|old_id| ---- -------- |1|1||2|2||3|3||4|1||5|2||6|3| ---- -------- 6rowsinset(0.00sec)

那样,运用编程代码很有可能不确定性如何连接旧数据信息。该表缺乏从old_id到初始表名的投射。

随后根据原表ID和原表名的投射关联创建多值数据库索引。比如,下列实例:

mysql>createtablen4(old_idint,old_namevarchar(64),primarykey(old_id,old_name));QueryOK,0rowsaffected(0.05sec)mysql>insertinton4selectid,'n1'fromn1unionallselectid,'n2'fromn2;QueryOK,6rowsaffected(0.02sec)Records:6Duplicates:0Warnings:0mysql>select*fromn4; -------- ---------- |old_id|old_name| -------- ---------- |1|n1||1|n2||2|n1||2|n2||3|n1||3|n2| -------- ---------- 6rowsinset(0.00sec)

最终的表构造,融合前边的2个表n3和n4,创建了一个新的表,该表具备新的自加上字段名外键约束,初始表ID和初始表名:

create table n5(idintunsignedauto_incrementprimarykey,old_idint,old_namevarchar(64),uniquekeyudx_old_id_old_name(old_id,old_name));

自然,数据信息引言转移的主题风格过长,这节没法探讨。

1.2 UUID为主导键。

UUID如同自加外键约束一样,能够确保外键约束的唯一性。殊不知,它是当然混乱的,随机生成的,占有了空的大容量。在MySQL中,char(36)用以储存UUID,沒有特别的UUID基本数据类型,例如这一字符串数组:‘7985847 c-7d 59-11ea-8 add-080027 c 52750’。因为InnoDB表的特点,大家应当防止应用char(36)来储存初始的UUID做为表的外键约束。

尽管UUID是混乱的,在空中间有消耗,可是当然偶然性的优点能够运用吗?

MySQL给予了下列优化方法,便于初始的UUID能够用以表外键约束:

涵数uuid_to_bin

涵数UUID_to_bin由MySQL给予,用以将UUID字符串数组更改成16字节数的二进制字符串数组。UUID种类类似一些数据库查询的种类,比如POSTGRESQL。涵数uuid_to_bin回到varbinary(16)的基本数据类型。

比如,表t_binary,

mysql>createtablet_binary(idvarbinary(16)primarykey,r1int,keyidx_r1(r1));QueryOK,0rowsaffected(0.07sec)mysql>insertintot_binaryvalues(uuid_to_bin(uuid()),1),(uuid_to_bin(uuid()),2);QueryOK,2rowsaffected(0.01sec)Records:2Duplicates:0Warnings:0mysql>select*fromt_binary; ------------------------------------ ------ |id|r1| ------------------------------------ ------ |0x412234A77DEF11EA9AF9080027C52750|1||0x412236E27DEF11EA9AF9080027C52750|2| ------------------------------------ ------ 2rowsinset(0.00sec)

涵数uuid_short

Varbinary(16)依然是混乱的,因此MySQL也保证了一个涵数UUID_short来转化成一个相近UUID的全局性ID,結果是INT64。实际计算方式如下所示:

(server _ id & 255)create table t _ uuid _ short(idbigintunnsignedprimarykey,r1int,keyidx _ R1(R1));QueryOK,0 rowsaffected(0.06秒)MySQL > insertintot _ uuid_short values(uuid _ short(),1),(uuid _ short(),2)QueryOK,2 rowsaffected(0.02秒)纪录:2反复:0警示:0mysql >挑选* fromt _ uuid _ short - - | id | R1 | - - - | 16743984358464946177 | 1 | | 16743984358464946178 | 2 | - 2行insert (0.00秒)能够见到,uuID_short转化成的统计数据是根据INT64排列的,因此这一块能够看做是自加id的填补提升,假如每秒钟启用频次低于16777

讲了这么多,大家简易认证一下前面的结果,做一个科学小实验。

下列试验涉及到四张表:

新创建 t_uuid: uuid 为主导键表 t_binary:varbinary(16) 为主导键表 t_uuid_short:bigint 为主导键新创建表 t_id:自增 ID 为主导键

略见一斑,创作考试成绩从差到好的差异是:t _ uuidt _ binaryt _ idt_uuid_short .使我们尝试看一下它是不是合乎预估。

二种新的报表构造:

mysql>createtablet_uuid(idchar(36)primarykey,r1int,keyidx_r1(r1));QueryOK,0rowsaffected(0.06sec)mysql>createtablet_id(idbigintauto_incrementprimarykey,r1int,keyidx_r1(r1));QueryOK,0rowsaffected(0.08sec)

撰写了一个简洁的sql语句来为这种表建立30W纪录。

DELIMITER$$CREATEPROCEDURE`ytt`.`sp_insert_data`(f_tbnameVARCHAR(64),f_numberINTUNSIGNED)BEGINDECLAREiINTUNSIGNEDDEFAULT0;SET@@autocommit=0;IFf_tbname='t_uuid'THENSET@stmt=CONCAT('insertintot_uuidvalues(uuid(),ceil(rand()*100));');ELSEIFf_tbname='t_binary'THENSET@stmt=CONCAT('insertintot_binaryvalues(uuid_to_bin(uuid()),ceil(rand()*100));');ELSEIFf_tbname='t_uuid_short'THENSET@stmt=CONCAT('insertintot_uuid_shortvalues(uuid_short(),ceil(rand()*100));');ELSEIFf_tbname='t_id'THENSET@stmt=CONCAT('insertintot_id(r1)values(ceil(rand()*100));');ENDIF;WHILEicallsp_insert_data('t_uuid',300000);QueryOK,0rowsaffected(5min23.33sec)mysql>callsp_insert_data('t_binary',300000);QueryOK,0rowsaffected(4min48.92sec)mysql>callsp_insert_data('t_id',300000);QueryOK,0rowsaffected(3min40.38sec)mysql>callsp_insert_data('t_uuid_short',300000);QueryOK,0rowsaffected(3min9.94sec)

第二,业务流程有关的特性应当被作为外键约束。

外键约束的制定规定易读性很强,类似学员的学籍号(招收年代 系院 技术专业),买东西订单信息编码等。事实上,不建议外键约束应用那样好用的业务流程字段名。您需要创立一个新的自加上外键约束或uuid_short()涵数字段名。具体的业务流程字段名并不是做为外键约束设计方案的,反而是变成一个常用的唯一索引。比如,表n5:

mysql>createtablen5(idintunsignedauto_incrementprimarykey,usernointunsigned,uniquekeyudx_userno(userno));QueryOK,0rowsaffected(0.08sec)

使用userno(客户编码)做为外键约束。假如业务流程端数据信息是不正确的,例如可能是教师的缘故键入了失误的数据信息,或是是业务管理系统的BUG造成键入了失误的数据信息,那麼不仅仅要变更键入的表的外键约束(这是一个聚集索引),还需要变更取决于这一表的全部子表。这实际上是一个非常好的新项目。可是,假如有一个与业务流程不相干的外键约束,则只必须变更业务流程字段名(二级数据库索引),不用变更取决于该表的子表。

这儿一般介绍一下MySQL外键约束的设计理念。假如您有任何的难题,请留言板留言,并请更正文章中的所有不够。

评论(0条)

刀客源码 游客评论