结论
一、表的设计合理化,符合三大范式(3NF)
二、添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
三、查询语句的优化
四、数据库的分库分表
五、使用存储过程(模块化编程,可以提高速度)
六、数据库硬件升级
七、使用缓存机制
一、表的设计合理化,符合三大范式(3NF)
数据库满足数据库的三大范式可以避免数据冗余。
简单来说三句话:
1NF、列不可分;
2NF、非主键列完全依赖主键,不存在部分依赖;
3NF、非主键列必须直接依赖主键,不存在传递依赖。
第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。
考虑这样一个表:【联系人】(姓名,性别,电话)
如果在实际场景中,一个联系人有家庭电话和公司电话,那么这种表结构设计就没有达到 1NF。要符合 1NF 我们只需把列(电话)拆分,即:【联系人】(姓名,性别,家庭电话,公司电话)。1NF 很好辨别,但是 2NF 和 3NF 就容易搞混淆。
◆ 第二范式(2NF):首先是 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
考虑一个订单明细表:【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName)。
因为我们知道在一个订单中可以订购多种产品,所以单单一个 OrderID
是不足以成为主键的,主键应该是(OrderID,ProductID)。显而易见
Discount(折扣),Quantity(数量)完全依赖(取决)于主键(OderID,ProductID),而
UnitPrice,ProductName 只依赖于 ProductID。所以 OrderDetail 表不符合 2NF。不符合 2NF
的设计容易产生冗余数据。
可以把【OrderDetail】表拆分为【OrderDetail】(OrderID,ProductID,Discount,Quantity)和【Product】(ProductID,UnitPrice,ProductName)来消除原订单表中UnitPrice,ProductName多次重复的情况。
◆ 第三范式(3NF):首先是 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。
考虑一个订单表【Order】(OrderID,OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity)主键是(OrderID)。
其中 OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity
等非主键列都完全依赖于主键(OrderID),所以符合 2NF。不过问题是
CustomerName,CustomerAddr,CustomerCity 直接依赖的是
CustomerID(非主键列),而不是直接依赖于主键,它是通过传递才依赖于主键,所以不符合 3NF。
通过拆分【Order】为【Order】(OrderID,OrderDate,CustomerID)和【Customer】(CustomerID,CustomerName,CustomerAddr,CustomerCity)从而达到 3NF。
二、添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
1.应考虑在 where 及 order by 涉及的列上建立索引。
2. 更新非常频繁的字段不适合创建索引。
3. 索引并不是越多越好。
大多数情况下索引能大幅度提高查询效率,但:
①数据的变更(增删改)都需要维护索引,因此更多的索引意味着更多的维护成本
②更多的索引意味着也需要更多的空间 (一本100页的书,却有50页目录?)
三、查询语句的优化
1.避免索引失效导致的全表扫描
应尽量避免在 where 子句中对字段进行 null 值判断。
应尽量避免在 where 子句中使用 != 或 <> 操作符。
应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引。
应尽量符合最左前缀原则。
应尽量避免like查询是以%开头。
具体的可以参考我这篇博客:索引失效的几种情况(MySQL)
2.SQL语句的规范
任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
...
四、数据库的分库分表
设置数据库主从复制状态下的读写分离,主库进行更新操作,从库进行查询操作。(可以使用中间件Mycat进行实现)
五、使用存储过程(模块化编程,可以提高速度)
什么是存储过程?
存储过程是对一些sql语句和控制语句的封装,客户端可以通过存储过程的名称对其进行调用。
存储过程分类
返回记录集的存储过程
返回数值的存储过程
行为存储过程(在数据库中的更新和删除操作)
六、数据库硬件升级
七、使用缓存机制
可以使用Redis作为缓存,避免所有请求都落在DB上。