mysql设计与优化
mysql设计与优化
标签:
背景描述:
我刚入职的时候,在项目中承担一些简单的工作,mysql只会增删改查
就基本可以实现业务功能。工作两年后,独立负责项目也有一段时间了,维护过许多项目,对数据库的设计优化也有更深入的认识。在此总结一点个人经验。
1.设计
我负责的项目中,有一部分是旧系统维护,在旧系统基础上增加新功能。改动往往涉及到数据库结构的修改,这时就非常考验原系统的数据库设计,好的表结构十分利于扩展维护;弱智一般的数据库设计,让你陷入无情无尽的加班。
1.1记录下修改数据库结构的操作
开发过程中,所有修改数据库结构、数据的操作要全部记下来,说明为什么修改、修改了哪些内容,还要记下操作的sql语句,方便迁移环境的时候快速、一致的部署。
1.2第三范式
“第三范式”是设计数据库的基本要求,强烈要求各位如果自己能设计数据库,必须完完全全的遵循第三范式。许多时候,第二范式就能解决问题。但是每一个开发人员都知道:第一个版本是最简单的版本,以后会有无穷无尽奇奇怪怪的需求出现。不把最基础的表结构设计好,以后的工作量会异常的大,因为你每次修改都要写一堆冗余代码来处理扩展过程中遇到的数据库设计不合理的问题,如果你的编码风格也不好,让这些冗余代码分散到整个项目的各个地方,那么你将获得一个如深渊般的恐怖代码,层出不穷的bug,混沌不堪的逻辑,把你拖入无尽的加班中。
针对不同的业务,设计数据库有不同的技巧(建议各位同学多学学前辈们在类似业务中的设计),难以一一列举。但是“第三范式”是一个高度抽象的标准,我越是被以前的垃圾代码困扰,越是深刻体会这个范式的正确性。
1.3一个表设计几个列
- 先从性能角度考虑一下
业务角度:
越少越好,如果实在是太多,建议拆分。原因如下:
(1)查询用到join,如果你一个关键的表总是几十个字段,还要用来做连接查询,肯定效率比字段少的要低;其次,表中的列越多,被使用的概率就越大,大量线程去使用一个很大的表,效率高就见鬼了。
(2)数据库的表往往对应实体类,列太多,就需要考虑更新时候哪些字段更新,哪些字段不更新的问题。如果是部分修改,一般都是先查出来,修改完数据之后再写回去。
看看以下场景:
1.一个很多列的表,只修改两三个字段,你需要完成:
select a,b,c,d,e,f,g from test where (.....);
update test(a,b,c,....,z) vaue(a,b,...,z)
2.上面那个很多列的表拆分成了若干表,只需改动其中一个:
update test_1(a,b,c) value (1,2,3)
两种情况下,第二种不但语句短(网络传递较快),而且执行起来比第一个更快。
而且,更细粒度的数据更有利于扩展。
1.4数据安全
安全策略、备份策略,老生常谈,不必再提。
2.优化
2.1尽量避免在 where 子句中对字段进行 null 值判断
否则将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where ais null
可以在 a 上设置默认值 0,确保表中 a 列没有 null 值,然后这样查询:
select id from t where a=0
2.2尽量避免在 where 子句中使用 or 来连接条件
否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where a=10 or a=20
可以这样查询:
select id from t where a=10 union all select id from t where a=20
2.3下面的查询也将导致全表扫描:
select id from t where name like‘%c%’
下面走索引
select id from t where name like‘c%’
若要提高效率,可以考虑全文检索。
2.4in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where a in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where a between 1 and 3
如果在 where 子句中使用参数,也会导致全表扫描。因为 SQL 只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where
可以改为强制查询使用索引:
select id from t with(index(索引名)) where
2.5尽量避免在 where 子句中对字段进行表达式操作
这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where a/2=100
应改为:
select id from t where a=100*2
2.6尽量避免在 where 子句中对字段进行函数操作
这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=‘abc‘ name以abc开头的id select id from t where datediff(day,createdate,‘2005-11-30‘)= 0 ‘2005-11-30‘生成的id
应改为:
select id from t where name like‘abc%’ select id from t where createdate>=‘2005-11-30′ and createdate<‘2005-12-1′
2.7复合索引
在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
2.8用 exists 代替 in
很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in (select num from b)
用下面的语句替换:
select num from a where exists (select 1 from b where num=a.num)
2.9并不是所有索引对查询都有效
SQL 是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL 查询可能不会去利用索引,如一表中有字段 sex,male、female 几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
2.10尽量使用数字型字段
若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会 逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
2.11尽可能的使用 varchar 代替 char
因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
2.12当只要一行数据时使用 Limit 1
当查询表已经知道结果只会有一条结果,在这种情况下,加上 Limit 1 可以增加性能。MySQ L数据库引擎会在找到一条数据后停止搜索,而不是继续往后查少下一条符合记录的数据。
2.13如果应用程序有很多 Join 查询,应该确认两个表中 Join 的字段是被建过索引的
这样,MySQL 内部会启动优化 Join 的 SQL 语句的机制。这些被用来 Join 的字段,应该是相同的类型的。例如:如果要把 DECIMAL 字段和一个 INT 字段 Join 在一起,MySQL 就无法使用它们的索引。对于那些 STRING 类型,还需要有相同的字符集才行。