MySQL数据库表的设计和优化(下)

2、基于单表设计的多表设计原则:
(1)表关系:
一)一对一关系:
定义:
在这种关系中,关系表的每一边都只能存在一个记录。每一个数据表中的关键字在对应的关系表中只能存在一个记录或者没有对应的记录。这种关系和一对配偶之间的关系很是类似——要么你已经结婚,你和你的配偶只能有一个配偶,要么你没有结婚没有配偶。大多数的一对一的关系都是某种商业规则约束的结果,而不是按照数据的天然属性来获得的。若是没有这些规则的约束,你一般能够把两个数据表合并进一个数据表,并且不会打破任何规范化的规则。
一对一关系又分为:一对一外键关联和一对一主键关联。
一对一主键关联:要求两个表的主键必须彻底一致,经过两个表的主键创建关联关系。
能够看到下图,很明显的,班级编号做为主键的话,就是一个主键关联了。

                        

 



一对一外键关联:
下面又很明显看到,以班主任ID做为外键关联起来的一个表。




二)一对多关系(多对一):
定义:
主键数据表中只能含有一个记录,而在其关系表中这条记录能够与一个或者多个记录相关,也能够没有记录与之相关。这种关系相似于你和你的父母之间的关系。你只有一位母亲,可是你母亲能够有几个孩子。
下图能够看到:一对多-班级表有多个学生;多对一-多个学生属于一个班级。

                     

   
三)多对多关系:
定义:
两个数据表里的每条记录均可以和另外一个数据表里任意数量的记录(或者没有记录)相关。例如,若是你有多个兄弟姐妹,这对你的兄弟姐妹也是同样(有多个兄弟姐妹),多对多这种关系须要引入第三个数据表,这种数据表称为联系表或者链接表,由于关系型系统不能直接实现这种关系。

在RDBMS中,必须使用中间表来表示多对多的关系。中间表咱们能够分红两种,一种是纯粹表示关系的中间表,一种是表示中间实体的中间表。

纯粹表示关系的中间表很简单,只须要两列:AID和BID,AID之外键关联到A表的主键,BID之外键关联到B表的主键,而后这两个列组成联合主键。这个中间表纯粹是表示多对多关系而存在,在业务上不会有对应的实体与之对应。好比前面提到的学生和课程的关系,若是咱们只须要知道哪些学生上哪些课,哪些课有哪些学生选,不须要有更多的信息的状况下,咱们就能够创建“学生课程”中间表,里面只有学生ID和课程ID两个字段。

             

        中间实体是在纯粹的中间关系表的基础上,加上了更多的属性,从而造成了一个新的实体。好比上面提到的学生和课程的关系,若是咱们须要记录学生选课的时间、学生选择这门课程后的考试成绩,那么咱们就像创建一个“选课”实体,
该实体具备以下属性:
选课ID,主键
学生ID,与学生表作外键关联
课程ID,与课程表作外键关联
选课时间,DateTime类型
考试成绩,记录选修该课程后考试的最终成绩

注意:
一)外键与索引:
外键是一种约束,与索引的概念不同,只是大多数状况下,咱们创建外键时,都会在外键列上创建对应的索引。外键的存在会在每一次数据插入、修改时进行约束检查,若是不知足外键约束,则禁止数据的插入或修改,这必然带来一个问题,就是在数据量特别大的状况下,每一次约束检查必然致使性能的降低。索引其实也有相似的问题,索引若是建多了,那么在插入删除修改数据时也要去维护对应的索引,因此索引的存在也会致使数据操做变慢。
不过外键与索引的优势不一样,外键只是保证数据的一致性,并不能给系统性能带来任何好处,因此因为外键致使的插入数据变慢会随着数据量的增加而愈来愈严重。而索引的目的是为了检索数据更快,维护数据时致使的索引数据的变动,对性能的影响不会像外键那样随着数据量增加而变得严重(固然大数量时的索引树维护会比小数据量的索引树维护更麻烦,但至少不是像外键那样)。
出于性能的考虑,若是咱们的系统彻底由咱们开发的程序使用,而不须要提供数据库给其余应用系统写入数据,并且对性能要求较高,那么咱们能够考虑在生产环境中不使用外键,只须要创建可以提升性能的索引。因为整个数据库的操做都是由咱们开发的程序来完成的,因此咱们程序能够在开发过程当中作好各方面的一致性检查,保证操做的数据是知足外键约束的,而不须要真正的存在这样一个外键约束。怎么作到这一点呢,首先,咱们在创建数据库时有多个脚本,包括建立表、建立初始化数据、建立索引、建立外键等,咱们在开发和测试环境中,都把这些脚本运行了,以使开发测试环境中的数据库是完整的,通过大量测试保证应用程序可以维护数据之间的约束的状况下,那么咱们在生产时,并不须要运行建立外键这个脚本文件,只须要建立表、初始化数据、建立索引等便可。
二)创建关系
在开始着手考虑创建关系表之间的关系以前,你可能须要对数据很是熟悉。只有在熟悉数据以后,关联会比你刚开始的时候更明显。你的数据库系统依赖于在两个数据表中找到的匹配值来创建关系。
进行匹配的值都是主键和外键的值。(关系模型不要求一个关系必须对应的使用一个主键来肯定。你可使用数据表中的任何备选关键字来创建关系,可是使用主键是你们都已经接受的标准。)主键(primary key)惟一的识别表中的每一个记录。而外键(foreign key)只是简单的将一个数据表中的主键存放在另一个数据表中。一样地,对于你来讲也不须要作太多的工做——只是简单地将主键加到关系表中,并将其定义为外键。

(2)分表原则:
分表主要目的是为突破单节点数据库服务器的 I/O 能力限制,解决数据库扩展性问题。 同时分表分库等思想也将引出之后的数据库集群,主从复制、读写分离方案…

为何咱们要分表分区???
平常开发中咱们常常会遇到大表的状况,所谓的大表是指存储了百万级乃至千万级条记录的表。这样的表过于庞大,致使数据库在查询和插入的时候耗时太长,性能低下,若是涉及联合查询的状况,性能会更加糟糕。分表和表分区的目的就是减小数据库的负担,提升数据库的效率,一般点来说就是提升表的增删改查效率。

(一)表拆分方式:
1)垂直切分:

定义:
把主键和一些数据表的列放在一个表中,而后把主键和另外一些数据表的列放在一个表中。
若是一个表的某些列经常使用,另外一些不经常使用,则能够采用垂直拆分。垂直拆分可使数据行变小,一个数据页就能够存放更多的数据,在查询时候能够减小I/O次数。其缺点是须要管理冗余列,查询全部数据时候须要join查找。
优势:
使得行数据变小,一个数据块(Block)就能存放更多的数据,在查询时就会减小I/O次数(每次查询时读取的Block 就少)。
能够达到最大化利用Cache的目的。

缺点:
表垂直分割后,主码(主键)出现冗余,须要管理冗余列
会引发表链接JOIN操做(增长CPU开销)须要从业务上规避

2)水平拆分(分表,分区)–按表中某一字段值的范围划分:


定义:
根据列的范围值进行合理切分,放在多个独立的表或分区中。

适用场景:
表很大,分割后能够下降查询时候须要读取的数据和索引的页数,同时下降索引的层数,提升查询速度。
表中的数据是独立的,例如表中分别记录各个地区的数据或不一样时期的数据,特别是有些数据经常使用,而另外一些数据不经常使用。
须要把数据放在多个存储介质上。
须要把历史数据和当前的数据拆分开。

例子:
当伴随着某一个表的数据量愈来愈大,以致于不能承受的时候,就须要对它进行进一步的切分。一种选择是根据key 的范围来作切分,譬如ID 为 1-10000的放到表A上,ID 为10000~20000的放到表B。这样的扩展就是可预见的。另外一种是根据某一字段值来划分,譬如根据用户名的首字母,若是是A-D,就属于表A,E-H就属于表B。这样作也存在不均衡性,当某个范围超出了单点所能承受的范围就须要继续切分。还有按日期切分等等。

可使用Mrg_Myisam引擎实现水平分表。
优势:
单表大小可控,自然水平扩展。下降在查询时须要读的数据和索引的页数,同时也下降了索引的层数,加快了查询速度。

缺点:
没法解决集中写入瓶颈的问题。同时,水平分割会给应用增长复杂度,它一般在查询时须要多个表名,查询全部数据须要union操做。在许多数据库应用中,这种复杂性会超过它带来的优势,由于只要索引关键字不大,则在索引用于查询时,表中增长两到三倍数据量,查询时也就增长读一个索引层的磁盘次数。

3)散列库表(基于hash算法的切分):
定义:
表散列与水平分割类似,但没有水平分割那样的明显分割界限,采用Hash算法把数据分散到各个分表中, 这样IO更加均衡。通常采用mod来切分,一开始肯定切分数据库的个数,经过hash取模来决定使用哪台。这种方法可以平均地来分配数据,可是伴随着数据量的增大,须要进行扩展的时候,这种方式没法作到在线扩容。每增长节点的时候,就须要对hash 算法从新运算。
咱们会按照业务或者功能模块将数据库进行分离,不一样的模块对应不一样的数据库或者表,再按照必定的策略对某个页面或者功能进行更小的数据库散列,好比用户表,按照用户ID进行表散列,散列128张表,则应就可以低成本的提高系统的性能而且有很好的扩展性

优势:
数据分布均匀

缺点:
数据迁移的时候麻烦,不能按照机器性能分摊数据

(二)在了解完分表了,咱们先来理解区分分区与分表吧。
分区:
定义:
分区和分表类似,都是按照规则分解表。不一样在于分表将大表分解为若干个独立的实体表,而分区是将数据分段划分在多个位置存放,能够是同一块磁盘也能够在不一样的机器。分区后,表面上仍是一张表,但数据散列到多个位置了。app读写的时候操做的仍是大表名字,db自动去组织分区的数据。

分表定义:
分表是将一个大表按照必定的规则分解成多张具备独立存储空间的实体表,咱们能够称为子表,每一个表都对应三个文件,MYD数据文件,.MYI索引文件,.frm表结构文件。这些子表能够分布在同一块磁盘上,也能够在不一样的机器上。app读写的时候根据事先定义好的规则获得对应的子表名,而后去操做它。

mysql分表和分区有什么联系呢?:
1.都能提升mysql的性高,在高并发状态下都有一个良好的表现。
2.分表和分区不矛盾,能够相互配合
,对于那些大访问量,而且表数据比较多的表,咱们能够采起分表和分区结合的方式(若是merge这种分表方式,不能和分区配合的话,能够用其余的分表试),访问量不大,可是表数据不少的表,咱们能够采起分区的方式等。

3.分表技术是比较麻烦的,须要手动去建立子表,app服务端读写时候须要计算子表名。采用merge好一些,但也要建立子表和配置子表间的union关系。
4.表分区相对于分表,操做方便,不须要建立子表。
(三)表拆分建议:(针对大系统)
其实这点没有明确的判断标准,比较依赖实际业务状况和经验判断。通常MySQL单表1000W左右的数据是没有问题的(前提是应用系统和数据库等层面设计和优化的比较好)。

1)对记录多的表进行拆分。(几百-上千万级别的表)
2)须要拆分的表分为动态表和相对静态表。动态表拆分到不一样库,静态表存在于公共库。从公共库同步到分库。实现表的链接。
3)按照年、月、地域等来分割,或者根据时间范围、和很固定又清晰的字段值范围等,具备肯定的分割标志来分割。
相关文章
相关标签/搜索