【MYSQL架构之三】Mysql数据库结构优化

目录

  • 影响Mysql数据库的因素
  • 数据库结构优化的目的
  • 数据库结构设计的步骤
  • 数据库设计范式
  • 简单案例
    • 需求分析
    • 逻辑设计
    • 物理设计

影响Mysql数据库的因素

1.服务器硬件
2.操作系统
3.mysql服务器配置
4.数据库结构(影响最大)

良好的数据库逻辑设计和物理设计是数据库获得高性能的基础

数据库结构优化的目的

1.减少数据的冗余 (有时候需要冗余)
2.尽量避免数据维护中出现 更新、插入、删除异常

  • 插入异常:如果表中的某个实体随着另一个实体而存在。
    eg.插入课程,但是没学生选。插入失败,因为study_id是主键的一部分。
  • 更新异常:如果更改表中的某个实体的单独属性时,需要对多行进行更新。
    eg.修改某门课程的学分,则需要update多行数据。
  • 删除异常:如果删除表中的某一实体则会导致其他实体的消失。
    eg.如果删除了带有数学课程的选课记录,则数学这门课的信息也就会被删除了。
    【MYSQL架构之三】Mysql数据库结构优化_第1张图片

3.节约数据存储空间

数据库结构设计的步骤

1.需求分析:全面了解产品设计的存储需求【可能导致频繁修改数据库结构、写维护异常、数据冗余】

  • 存储需求:数据库需要存储什么样的数据,这些数据具有什么样的特点。
  • 数据处理需求:我们需要如何对数据库进行读取或修改已完成产品设计的功能;以及对数据处理的响应时间要求;数据处理的方式是批量处理还是联机处理。
  • 数据的安全性和完整性需求:包括生命周期的完整性。

2.逻辑设计:设计数据的逻辑存储结构

  • 数据实体之间的逻辑关系
  • 解决数据冗余和数据维护异常

3.物理设计:根据所使用的数据库特点进行表结构设计

  • 关系型数据库:Oracle,SQLServer,MySQL,postgresSQL
  • 非关系型数据库:mongo,Redis,Hadoop
  • 存储引擎:Innodb
  • 字段类型

4.维护优化:根据实际情况对索引、存储结构等进行优化

数据库设计范式

设计出没有数据冗余和数据维护异常的数据库结构
1.第一范式

  • 数据库表中的所有字段都只具有单一属性(即字段不能再分割了)
  • 单一属性的列是由基本的数据类型所构成的
  • 设计出来的表都是简单的二维表

2.第二范式

  • 要求一个表中只具有一个业务主键,也就是说符合第二范式的表中不能存在非主键列对只对部分主键的依赖关系。
  • 第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

3.第三范式

  • 指每一个非主属性既不部分依赖于也不传递依赖于业务主键,也就是在第二范式的基础上消除了非主属性对主键的依赖传递。
  • 首先要满足第二范式,其次非主属性之间不存在函数依赖。由于满足了第二范式,表示每个非主属性都函数依赖于主键。如果非主属性之间存在了函数依赖,就会存在传递依赖,这样就不满足第三范式。
    【MYSQL架构之三】Mysql数据库结构优化_第2张图片
    以上图片满足第二范式,但是不满足第三范式。因为学生的学号可以确定学院,而学院和学院的电话又有关系,也就是说学院学院电话对学号来说是具有传递依赖关系的。

  • 总结:
    第一范式就是原子性,字段不可再分割;
    第二范式就是完全依赖,没有部分依赖;
    第三范式就是没有传递依赖

简单案例

一、需求分析

按下面的需求设计一个电子商务网站的数据库结构

  1. 本网站只销售图书类商品
  2. 需要具有以下功能:用户登录、用户管理、商品展示、商品管理、供应商管理、在线销售。

分析:

  • 用户登录和用户管理功能
    • 用户必须注册并登录系统才能进行网上交易。
    • 同一时间同一用户只能在一个地方登录。【安全性】
    • 用户信息:{用户名、密码、手机号、姓名、注册日期、在线状态、出生日期}
      【只有一个业务主键,一定符合第二范式;没有属性和业务主键存在传递依赖的关系,符合第三范式】
  • 商品展示和商品管理功能
    • 商品信息:{商品名称,分类名称,出版社名称,图书价格,图书描述,作者}
      【商品名称和分类名称不符合第二范式,没用到的分类就会没有分类信息】
      ①商品信息:{商品名称,出版社名称,图书价格,图书描述,作者}
      ②分类信息:{分类名称,分类描述}
      ③商品分类(对应关系表):{商品名称,分类名称}
  • 供应商管理功能
    • 供应商信息:{出版社名称,地址,电话,联系人,银行账号}
  • 在线销售功能
    • 在线销售:{订单编号,下单用户名,下单日期,订单金额,订单商品分类,订单商品名,订单商品数量,支付金额,物流单号}
      ①只有一个业务主键,符合第二范式。
      ②订单商品单价,订单商品数量,订单编号 存在着传递依赖关系,不符合第三范式。
      ③数据冗余:订单商品信息和商品信息信息表中的数据
    • 进行拆分
      ①订单表:{订单编号,下单用户名,下单日期,订单金额,支付金额,物流单号}
      ②订单商品关联表:{订单编号订单商品分类订单商品名,商品数量}

二、逻辑设计

  • 场景案例一:
    【MYSQL架构之三】Mysql数据库结构优化_第3张图片
    存在的问题:查询量大;如果商品价格变动,这个总金额也会变动,不符合业务。

  • 场景案例二:假设下单用户就是商品的收货人,我们在发货前一定要查询出每个订单的下单人的信息,而这些信息全部记录在用户信息表中。
    【MYSQL架构之三】Mysql数据库结构优化_第4张图片
    完全符合范式化的设计有时并不能得到良好的SQL查询性能

反范式化设计

  • 反范式化就是为了性能和读取效率的考虑而适当的对数据库设计范式的要求进行违反,而允许存在少量的数据冗余,也就是使用空间来换取(查询)时间
  • 不能要求完全按照范式化的要求进行数据库设计,要考虑以后表的使用(查询等)
    举个例子:比如订单表中应该保留当前购买商品的价格、商品的名称(商品的价格是会变动的,这很危险)

反范式化情景案例:

  • 对商品信息表进行反范式化改造
    • 考虑:基本每次查询商品的时候都会用到其分类。为防止插入异常,会保留分类信息表。
    • ①商品信息:{商品名称分类名称,出版社名称,图书价格,图书描述,作者}
      ②分类信息:{分类名称,分类描述}
  • 对在线销售表进行反范式化改造
    • 考虑:如果要获取用户的手机号就必须关联用户表,如果改变了手机号,查询订单时就不是以前的手机号了;订单金额会随商品价格而改变;
      【MYSQL架构之三】Mysql数据库结构优化_第5张图片

SQL场景:

  • 查询出每一个用户的订单总金额
    select 下单用户名,sum(订单金额) from 订单表 group by 下单用户名;

  • 查询出下单用户和订单详情
    select a.订单编号,a.用户名,a.手机号,b.商品名称,b.商品单价,b.商品数量 from 订单表 a join 订单商品关联表 b on a.订单编号=b.订单编号;

范式化设计和反范式化设计的优缺点

范式化设计 反范式化设计
优点
  • 可以尽量的减少数据冗余
  • 范式化的更新操作比反范式化更快
  • 范式化的表通常比反范式化更小
  • 可以减少表的关联
  • 可以更好的进行索引优化
缺点
  • 对于查询需要关联多个表
  • 更难进行索引优化
  • 存在数据冗余和数据维护异常
  • 对数据修改需要更多的成本

三、物理设计

  • 定义数据库、表及字段的命名规范
    • 可读性原则
    • 表意性原则(采用英文,且意思准确)
    • 长名原则(尽量不要使用缩写)
  • 选择合适的存储引擎
    【MYSQL架构之三】Mysql数据库结构优化_第6张图片
  • 为表中的字段选择合适的数据类型

    • 当一个列可以选择多种数据类型时,应该优先考虑数字类型,其次是日期类型或二进制类型,最后是字符类型。
    • 对于相同级别的数据类型,应该优先选择占用空间小的数据类型

    【分析:在数据比较时,如排序等,字符数据是与排序规则相关的,而二进制是不需要数据字典的,按照大小排列;字符串数据往往比二进制数据处理得慢;数据库中数据处理是以页为单位的,每个页能存储的数据量是一定的,Innodb中是16k,页的长度越小,能容纳的页的行数就越多,加载宽度小的页要比宽度大的页所用时间少,并减少了磁盘IO,性能提升】
    (1)整数类型的选择
    【MYSQL架构之三】Mysql数据库结构优化_第7张图片
    误区:int(2)指定int宽度,这个是无意义的,int还是4字节。
    (2)实数类型的选择
    【MYSQL架构之三】Mysql数据库结构优化_第8张图片

    • DECIMAL(18,9) 需要9个字节来存储
    • DECIMAL可以保证精度,但是占用空间大,MYSQL5.0之后DECIMAL最多只能存储65个数字,应该也足够了。
    • DECIMAL应用场景:财务相关

(3)varchar和char类型的选择

  • varchar和char是以字符为单位的,不是字节。一个字符可能占用多个字节。
    【utf-8字符集为例,误以为在varchar中存储10个字符就需要varchar列的宽度是30,实际只需要varchar(10)即可】
  • VARCHAR长度的选择问题
    • 使用最小的符合需求的长度
      【姓名:中文10个字符足够,外国20个字符足够,不该255】
      【一旦业务上线,修改长度的成本很大。MYSQL5.7之前是要锁表的;MYSQL5.7之后如果改前小于255,改后也小于255,是可以不锁表的。】
    • varchar(5)和varchar(200)性能不同,长度越长,消耗内存更大
VARCHAR类型 CHAR类型
特点
  • varchar用于存储变长字符串,只占用的必要的存储空间
  • 列的最大长度小于255则只占用一个额外字节用于记录字符串长度
  • 列的最大长度大于255则要占用两个额外字节用于记录字符串长度
  • char类型是定长的
  • 字符串存储在char类型的列中会删除末尾的空格
  • char类型的最大宽度为255
使用场景
  • 字符串列的最大长度比平均长度大很多
  • 适用于存储很少被更新的字符串列(因为长度可变,可能引起存储页的分裂,造成很多存储碎片)
  • 使用了多字节字符集(如utf8)存储字符串
  • 适合存储长度近似的值(如MD5值,手机号,身份证号)
  • 适合存储短字符串,节省空间(如性别:以utf-8为例,char类型需要3个字节,varchar类型需要4个字节,因为用一个记录长度)
  • 适合存储经常更新的字符串(长度固定)

(4)日期类型的选择

  • DATATIME类型
    • 以YYYY-MM-DD HH:MM:SS[.fraction]格式存储日期时间datetime = YYYY-MM-DD HH:MM:SS
    • 在MYSQL5.6之后,支持存储微秒级别。默认保存到秒。
    • 想在列中保留6位微秒:datetime(6) = YYYY-MM-DD HH:MM:SS.fraction
    • DATETIME类型与时区无关,占用8个字节的存储空间。
    • 时间范围1000-01-01 00:00:00到9999-12-31 23:59:59
  • TIMESTAMP类型
    • timestamp类型占用4个字节,显示依赖于所指定的时区
    • 范围为1970-01-01 08:00:01到2038-01-19 11:14:07
    • TIMESTAMP支持的范围比DATATIME要小,容易出现超出的情况
    • TIMESTAMP类型在默认情况下,insert、update 数据时,TIMESTAMP列会自动以当前时间(CURRENT_TIMESTAMP)填充/更新。[只有第一个timestamp列自动填充/更新,当然也可以通过建表语句指定某一列自动更新]
    • TIMESTAMP比较受时区timezone的影响以及MYSQL版本和服务器的SQL MODE的影响
    • 显示微秒:timestamp(6)
  • DATA类型和TIME类型
    存储用户生日时,只需要存储日期部分。之前往往三种处理方案:
    ①把日期部分存储为字符串(至少要8个字)
    ②使用int类型来存储(4个字节)
    ③使用datetime类型来存储(8个字节)
    MYSQL5.7之后,可以用date类型。

    • 占用的字节数比使用字符串、datetime、int存储要少,使用date类型只需要3个字节。
    • 使用date类型还可以利用日期时间函数进行日期之间的计算
    • DATE类型仅有日期值,没有时间部分。以’YYYY-MM-DD’格式显示DATE值,支持的范围是’1000-01-01’到’9999-12-31’。
    • TIME数据类型表示一天中的时间。以”HH:MM:SS”格式显示TIME值。支持的范围是’00:00:00’到’23:59:59’。(通过定义时间宽度设置微秒)
  • 使用日期时间存储类型注意事项

    • 不要使用字符串类型来存储日期、时间数据,日期时间类型通常比字符串占用更少的存储空间
    • 日期时间类型在进行查找过滤时,可利用日期来进行对比
    • 日期时间类型有丰富的处理函数,可以方便的对日期类型进行计算
    • 使用int存储日期时间,不如使用timestamp

物理设计总结:
如何选择表的主键?
如何为Innodb选择主键?

  • 主键应该尽可能的小(Innodb中,每一个非主键的索引都会附加主键的信息,如果主键较大的话,索引就会变大,效率降低)
  • 主键应该是顺序增长的(顺序插入,避免随机IO的产生,增加数据的插入效率;如果主键不顺序增长,每次插入就可能重新排序,带来额外的IO消耗,效率降低)
  • Innodb的主键和业务主键可以不同(为了保证业务主键的唯一性,可以在业务主键上增加一个唯一索引)

转载请标明出处:https://blog.csdn.net/it_lyd/article/details/80277692

你可能感兴趣的:(⊹●MySQL,MySQL数据库)