(5)MySQL进阶篇SQL优化(优化数据库对象)

1.概述

在数据库设计过程当中,用户可能会常常遇到这种问题:是否应该把全部表都按照第三范式来设计?表里面的字段到底改设置为多大长度合适?这些问题虽然很小,可是若是设计不当则可能会给未来的应用带来不少的性能问题。本章中将介绍MySQL中一些数据库对象的优化方法,其中一些方法不只仅适用于MySQL,也适用于其余类型的数据库管理系统。数据库

2.优化表的数据类型

表须要使用任何的数据类型,是须要根据应用程序来判断的。虽然应用程序设计的时候须要考虑字段的长度留有必定的冗余,可是不推荐让不少字段都留有大量的冗余,这样即浪费磁盘存储空间,同时在应用程序操做时也浪费物理内存。在MySQL中,可使用函数PROCEDURE ANALYSE()对当前应用的表进行分析,该函数能够对数据表中列的数据类型提出优化建议,用户能够根据应用的实际状况酌情考虑是否实施优化。如下是函数PROCEDURE ANALYSE()的使用方法:数据库设计

SELECT * FROM tbl_name PROCEDURE ANALYSE();
SELECT * FROM tbl_name PROCEDURE ANALYSE(16,256);

可是我在8.0以上版本的MySQL上执行这条语句是报错的,具体是8.0版本不支持仍是改变语法,这边就暂时不深究了,有空再查官网资料了解下。
函数

3.经过拆分提升表的访问效率

这里所说的“拆分”,是指对数据表进行拆分。若是针对MyISAM类型的表进行,那么有两种拆分方法。
第一种方法是垂直拆分,即把主键和一些列放到一个表,而后把主键和另外的列放到另外一个表中。
优势:若是一个表中某些列经常使用,而另一些列不经常使用,则能够采用垂直拆分,另外垂直拆分可使得数据行变小,一个数据页就能存放更多的数据,在查询时就会减小I/O次数。
缺点:须要管理冗余列,查询全部数据须要联合(JOIN)操做。
第二种方法是水平拆分,即根据一列或多列数据的值把数据行放到两个独立的表中。水平拆分一般在如下几种状况下使用:
◎表很大,分割后能够下降在查询时须要读的数据和索引的页数,同时也下降了索引的层数,提升查询速度。
◎表中的数据原本就有独立性,例如,表中分别记录各个地区的数据或不一样时期的数据,特别是有些数据经常使用,而另一些数据不经常使用。
◎须要把数据存放到多个介质上。例如,移动电话的帐单表就能够分红两个表或多个表。最近3个月的帐单数据存在一个表中,3个月前的历史帐单存放在另一个表中,超过1年的历史帐单能够存储到单独的存储介质上,这种拆分是最常使用的水平拆分方法。
注:水平拆分会给应用系统增长复杂度,它一般在查询时须要多个表名,查询全部数据须要UNION操做。在许多数据库应用中,这种复杂性会超过它带来的优势,由于只要索引关键字不大,则在索引用于查询时,表中增长2至3倍数据量,查询时也就增长读一个索引层的磁盘次数,因此水平拆分要考虑数据量的增加速度,根据实际状况决定是否须要对表进行水平拆分。性能

4.逆规范化

数据库设计时要知足规范化这个道理你们都很是清楚,可是否数据的规范化程度越高越好呢?这仍是由实际需求来决定。由于规范化越高,那么产生的关系就越多,关系过多的直接结果就是致使表之间的链接操做越频繁,而表之间的链接操做是性能较低的操做,直接影响到查询的速度,因此对于查询较多的应用程序就须要根据实际状况运用逆规范化对数据进行设计,经过逆规范化来提升查询的性能。
例如,通常电商平台后台的库存列表都会包含商家名称等信息,方便管理人员查看,假设商家ID、名字和其余属性都存放在一个商家信息表A中,而库存型号、品牌、库存量、商家ID和其余属性都存放在一个库存信息表B中,那么咱们在库存列表中要显示商家名称等信息时,就必需要在数据库中进行表链接,由于库存信息表B中并不包含商家信息表A的商家名称等信息,因此必须经过关联A表把数据取过来,若是在数据库设计时就考虑到这一点,就能够在B表增长一个冗余字段存放商家名字数据,这样在查询库存列表时就不用再作表关联了,这样也可使查询有更好的性能。
反规范的好处是下降链接操做的需求、下降外键和索引的数目,还可能减小表的数目,相应带来的问题是可能出现数据的完整性问题。加快查询速度,但会下降修改速度。所以决定作反规范时,必定要权衡利弊,仔细分析应用程序的数据存取需求和实际的性能特色,好的索引和其余方法常常可以解决性能问题,而没必要采用反规范这种方法。
在进行反规范操做以前,要充分考虑数据的存取需求、经常使用表的大小、一些特殊的计算(例如合计)、数据的物理存储位置等。经常使用的反规范技术有增长冗余列、增长派生列、从新组表和分割表。
◎增长冗余列:指在多个表中具备相同的列,它经常使用来在查询时避免链接操做。
◎增长派生列:指增长的列来自其余表中的数据,由其余表中的数据通过计算生成。增长的派生列其做用是在查询时减小链接操做,避免使用集函数。
◎从新组表:指若是许多用户须要查看两个表链接出来的结果数据,则把这两个表从新组成一个表来减小链接而提升性能。
◎分割表:能够参见3小节“经过拆分提升表的访问效率”的内容。
另外,逆规范技术须要维护数据的完整性。不管使用何种反规范技术,都须要必定的管理来维护数据的完整性,经常使用的方法是批处理维护、应用程序逻辑和触发器。
◎批处理维护是指对复制列或派生列的修改积累必定的时间后,运行一批处理做业或存储过程对复制或派生列进行修改,这只能在对实时性要求不高的状况下使用。
◎数据的完整性也可由应用程序逻辑来实现,这就要求必须在同一事务中对全部涉及的表进行增、删、改操做。用应用程序逻辑来实现数据的完整性风险较大,由于同一逻辑必须在全部的应用程序中使用和维护,容易遗漏,特别是在需求变化时,不易于维护。
◎另外一种方式就是使用触发器,对数据的任何修改当即触发对复制列或派生列的相应修改。触发器是实时的,并且相应的处理逻辑只在一个地方出现,易于维护。通常来讲,是解决这类问题比较好的办法。优化

5.使用中间表提升统计查询速度

对于数据量较大的表,在其上进行统计查询一般会效率很低,而且还要考虑统计查询是否会对在线的应用程序产生负面影响。一般在这种状况下,使用中间表能够提升统计查询的效率,下面经过对goods_stock库存表的统计来介绍中间表的使用,goods_stock表记录了天天库存导入记录,表结构以下:spa

DESC goods_stock;


因为天天都会产生大量的库存导入记录,因此goods_stock表的数据量很大,致使分页查询库存导入数据很慢:

如今业务部门有一具体的需求:但愿了解最近30天入库总库存量。针对这一需求咱们经过两种方法来得出业务部门想要的结果。
方法1:在goods_stock表上直接进行统计,得出想要的结果:设计

SELECT SUM(StockQty) AS TotalStockQty FROM goods_stock WHERE CreateTime>ADDDATE(NOW(),-30);


方法2:建立中间表tmp_goods_stock,表结构和源表结构彻底相同:code

CREATE TABLE `tmp_goods_stock`  (
  `ID` bigint NOT NULL AUTO_INCREMENT COMMENT '供应商库存ID',
  `StockGUID` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '用于与子表对应',
  `Model` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '型号',
  `Brand` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '品牌,标准名称',
  `LotNO` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '' COMMENT '批次,通常用四位年份+两位周,如:202024',
  `MinPackageQty` int NOT NULL DEFAULT 0 COMMENT '最小包装量',
  `PackageUnit` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '包装单位,包装类型',
  `StockQty` int NOT NULL DEFAULT 0 COMMENT '库存量,数量单位:PCS',
  `MinOrderQty` int NOT NULL DEFAULT 0 COMMENT '最小起订量',
  `IncQty` int NOT NULL DEFAULT 0 COMMENT '增量',
  `StockType` smallint NOT NULL COMMENT '物料类型 10:供应商现货 20供应商排单 30 云仓现货',
  `CreateTime` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
  `UpdateTime` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0),
  PRIMARY KEY (`ID`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 424449 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '商品库存信息,供应商物料库存' ROW_FORMAT = Dynamic;

转移要统计的数据到中间表,而后在中间表上进行统计,得出想要的结果:对象

INSERT INTO tmp_goods_stock SELECT * FROM goods_stock WHERE CreateTime>ADDDATE(NOW(),-30);

SELECT SUM(StockQty) AS TotalStockQty FROM tmp_goods_stock;


从上面的两种实现方法上看,在中间表中作统计花费的时间不多(这里不计算转移数据花费的时间),另外,针对业务部门想了解“但愿了解最近30天入库总库存量”这一需求,在中间表上给出统计结果更为合适,缘由是源数据表(goods_stock表)CreateTime字段并无索引而且源表的数据量较大,因此在按时间进行分时段统计时效率很低,这时能够在中间表上对CreateTime字段建立单独的索引来提升统计查询的速度。中间表在统计查询中常常会用到,其优势以下:
中间表复制源表部分数据,而且与源表相“隔离”,在中间表上作统计查询不会对在线应用程序产生负面影响。
中间表上能够灵活的添加索引或增长临时用的新字段,从而达到提升统计查询效率和辅助统计查询做用。blog

6.总结

本章节介绍了对数据库对象的优化,数据库对象设计的好坏是一个数据库设计的基础,并且一旦数据库对象设计完毕并投入使用,未来再进行修改就比较麻烦,所以在进行数据库设计的时候必定要尽量地考虑周到。参考文献:深刻浅出MySQL大全

相关文章
相关标签/搜索