二、关系型数据库优化-单机优化

1.范式和存储引擎

1.1范式

表的范式,是首先符合1NF, 才能满足2NF , 进一步满足3NF

1NF:即表的列的具有原子性,不可再分解,即列的信息,不能分解.只要数据库是关系型数据库(mysql/oracle/db2/sysbase/sql server),就自动的满足1NF.关系型数据库中是不允许分割列的

2NF:表中的记录是唯一的.通常我们设计一个主键来实现

3NF:即表中不要有冗余数据, 就是说,表的信息,如果能够被推导出来,就不应该单独的设计一个字段来存放.举例说明不满足:学生、班级
在这里插入图片描述
反3NF:没有冗余的数据库表未必是最好的数据库表,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。
举例说明:商品和商品分类
在这里插入图片描述
商品类的浏览量是取商品下面的所有浏览量之和,这种我们本来可以通过cid去查出来在进行求和,但是这样会使我们的运行效率变低,所以需要降低范式标准,在商品类中增加一个浏览量总和,在通过数据库触发器去完成.

  • 数据库触发器
    什么是触发器:触发器是与表有关的数据库对象,在满足定义条件时触发,并执行触发器中定义的语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性。
    创建触发器语法:
CREATE TRIGGER trigger_name trigger_time trigger_event ON tb_name FOR EACH ROW trigger_stmt
trigger_name:触发器的名称
tirgger_time:触发时机,为BEFORE或者AFTER
trigger_event:触发事件,为INSERTDELETE或者UPDATE
tb_name:表示建立触发器的表明,就是在哪张表上建立触发器
trigger_stmt:触发器的程序体,可以是一条SQL语句或者是用BEGINEND包含的多条语句
所以可以说MySQL创建以下六种触发器:
BEFORE INSERT,BEFORE DELETE,BEFORE UPDATE
AFTER INSERT,AFTER DELETE,AFTER UPDATE

其中,触发器名参数指要创建的触发器的名字
BEFORE和AFTER参数指定了触发执行的时间,在事件之前或是之后
FOR EACH ROW表示任何一条记录上的操作满足触发事件都会触发该触发器
创建有多个执行语句的触发器:

CREATE TRIGGER 触发器名 BEFORE|AFTER 触发事件
ON 表名 FOR EACH ROW
BEGIN
    执行语句列表
END

其中,BEGIN与END之间的执行语句列表参数表示需要执行的多个语句,不同语句用分号隔开
在BEGIN…END语句中也可以定义变量,但是只能在BEGIN…END内部使用:

DECLARE var_name var_type [DEFAULT value] #定义变量,可指定默认值
SET var_name = value  #给变量赋值

NEW和OLD的使用:请添加图片描述
根据以上的表格,可以使用一下格式来使用相应的数据:

NEW.columnname:新增行的某列数据
OLD.columnname:删除行的某列数据

举例:
在这里插入图片描述
创建触发器:

# 创建触发器
DELIMITER $
CREATE TRIGGER upd_goods AFTER UPDATE ON t_goods FOR EACH ROW
BEGIN
DECLARE s1 INT;
SET s1 = NEW.views - OLD.views;
UPDATE t_class SET views=views+s1 WHERE id = NEW.cid;
END $
DELIMITER ;

修改数据查看效果:

update t_goods set views=views+10 where id = 1;

在这里插入图片描述

1.2存储引擎-创建表时要选择存储引擎

mysql存储引擎分类:myisam,innodb,memory

  • 优缺点对比(MyISAM 和 INNODB的区别(主要))
    • 事务安全 MyISAM不支持事务,INNODB支持
    • 查询和添加速度 MyISAM速度快,INNODB速度慢
    • 支持全文索引 MyIsam支持,innodb不支持
    • 锁机制 MyIsam表锁 innodb行锁
    • 外键 MyISAM 不支持外键约束, INNODB支持外键. (通常不设置外键,通常是在程序中保证数据的一致)
  • 使用场景
    • MyISAM存储引擎: 如果表对事务要求不高,同时是以查询和添加为主的,我们考虑使用myisam存储引擎. 比如 bbs 中的 发帖表,回复表.
    • INNODB存储引擎:对事务要求高,保存的数据都是重要数据,我们建议使用INNODB,比如订单表,账号表.
    • Memory 存储:比如我们数据变化频繁,不需要入库,同时又频繁的查询和修改,我们考虑使用memory, 速度极快.
  • 操作
    创建表示指定存储引擎:Create table 表名(字段列表) engine 存储引擎名称;
    修改存储引擎:alter table table_name engine=innodb;

2.索引

2.1引入

说起提高数据库性能,索引是最物美价廉的东西了。不用加内存,不用改程序,不用调sql,只要执行个正确的‘create index’,查询速度就可能提高百倍千倍,这可真有诱惑力。可是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的I/O。
以空间换时间

2.2操作

mysql索引的分类标准:按照字段是否唯一或为null

  • 普通索引:允许重复的值出现,可以在任何字段上面添加
  • 唯一索引:除了不能有重复的记录外,其它和普通索引一样,可以在值是唯一的字段添加(用户名、手机号码、身份证、email,QQ),可以为null,并且可以有多个null
  • 主键索引:是随着设定主键而创建的,也就是把某个列设为主键的时候,数据库就会給改列创建索引。这就是主键索引.唯一且没有null值
  • 全文索引:用来对表中的文本域(char,varchar,text)进行索引, 全文索引针对MyISAM有用
    一般不用,默认只支持英文. -使用ES,Lucene代替就ok
2.2.1添加
  • 普通索引:允许重复的出现,一般来说,普通索引的创建,是先创建表,然后在创建普通索引
create index 索引名 on(1,列名2,...);
alter table 表名add index 索引名(1,列名2,..);
比如:
   create table aaa(id int unsigned,name varchar(32));
   create index nameIndex on aaa(name);
   alter table aaa add index index1(name);
  • 唯一索引:除了不能有重复的记录外,其它和普通索引一样
    • 当表的某列被指定为unique约束时,这列就是一个唯一索引
      例如:create table bbb(id int primary key auto_increment , name varchar(32) unique);
      这时, name 列就是一个唯一索引.
    • 在创建表后,再去创建唯一索引
      create unique index 索引名 on 表名 (列1,列2,…);
      alter table 表名add unique index 索引名 (列1,列2,…);
      例如:
      create table ccc(id int primary key auto_increment, name varchar(32));
      注意:unique字段可以为NULL,并可以有多NULL, 但是如果是具体内容,则不能重复.主键字段,不能为NULL,也不能重复.
  • 主键索引:是随着设定主键而创建的,也就是把某个列设为主键的时候,数据库就会給改列创建索引。这就是主键索引.唯一且没有null值
    • 创建表时指定主键
      例如:create table ddd(id int unsigned primary key auto_increment ,name varchar(32) not null defaul ‘’);
      这时id 列就是主键索引.
    • 如果你创建表时,没有指定主键,也可以在创建表后,再添加主键。
      指令:alter table 表名 add primary key (列名);
      举例: create table eee(id int , name varchar(32) not null default ‘’);
      alter table eee add primary key (id);
  • 全文索引:用来对表中的文本域(char,varchar,text)进行索引, 全文索引针对MyISAM有用(这里就不做多说明,我一般用es,如果确实需要使用可以上网查询)
2.2.2查询
show index from 表名

在这里插入图片描述
Table:表名
Non_unique:如果索引不能包括重复词,则为0。如果可以,则为1。
Key_name:索引的名称。
Seq_in_index:索引中的列序列号,从1开始。
Column_name:列名称。
Collation:列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。
Cardinality:索引中唯一值的数目的估计值。通过运行ANALYZE TABLE或myisamchk -a可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机 会就越大。
Sub_part:如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。
Packed:指示关键字如何被压缩。如果没有被压缩,则为NULL。
Null:如果列含有NULL,则含有YES。如果没有,则该列含有NO。
Index_type:用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。

2.2.3删除
alter table 表名 drop index 索引名; 
alter table 表名 drop primary key 删除主键。
# 注意: [主键定义为auto_increment时不能删除]
2.2.4修改

先删除后添加=修改

2.3注意事项

  • 使用索引的代价:
    • 占用磁盘空间
    • 对dml操作有影响,因为要维护索引,变慢
  • 什么列适合添加索引呢
    • 较频繁的作为查询条件字段应该创建索引(编号)
      select * from emp where empno = 1
    • 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件(性别)
      select * from emp where sex = '男’
    • 更新非常频繁的字段不适合创建索引(浏览量)
      select * from t_class where views = 300
    • 小结:满足以下条件的字段,才应该创建索引.
      • 肯定在where条件经常使用 或者经常用来排序 order by后面的字段
      • 该字段的内容不是唯一的几个值(sex)
      • 字段内容不是频繁变化.
      • 不会出现在WHERE子句中字段不该创建索引
      • 经常查询 很少修改 唯一性不要太差

3.SQL优化技巧

3.1DDL优化–批量插入

  • 通过禁用索引来提供导入数据性能 。 这个操作主要针对有数据库的表,追加数据
//去除键
alter table test3 DISABLE keys;
//批量插入数据
insert into test3 select * from test;
//恢复键
alter table test3 ENABLE keys;
# 把需要多次变化索引维护为一次,提升批量导入性能
  • 关闭唯一校验
#  关闭
set unique_checks=0
//批量插入数据
insert into test3 select * from test;
#   开启
set unique_checks=1
# 把多次唯一校验改为一次,提升插入性能
  • 修改事务提交方式(导入)
#   关闭
set autocommit=0 
//批量插入操作
insert into test3 select * from test;
#   开启
set autocommit=1 
# 把多次事务提交变为一次事务提交,提升插入性能

3.2DML优化–批量插入

# 多条新增数据
insert into test values(1,2);
insert into test values(1,3);
insert into test values(1,4);
//合并多条为一条
insert into test values(1,2),(1,3),(1,4)
# 多条数据的新增改为一次性新增

3.3DQL优化

  • order by优化
    • 多用索引排序
    • 普通结果排序(非索引排序)Filesort
    • 索引本身就是排序的,所以多使用索引。 如果某字段要排序,可以创建索引来提高效率
  • or优化
    • or两边都是用索引字段做判断,性能好
    • or两边,有一边不用,性能差
    • 如果employee表的name和email这两列是一个复合索引,但是如果是 :name=‘A’ OR email=‘B’ 这种方式,不会用到索引!
      sql语句的执行顺序
      from where having group by select order by limit
30sql语句优化如下:
1.’对查询进行优化,应尽量避免全表扫描,首先应考虑在 whereorder by 涉及的列上建立索引。
2.应尽量避免在 where 子句中使用!=<>操作符,否则将引擎放弃使用索引而进行全表扫描。
3.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.下面的查询也将导致全表扫描:
select id from t where name like '%abc%'
若要提高效率,可以考虑全文检索。
6.innot in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
9.应尽量避免在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'
10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
12.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(...)
13.很多时候用 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)
14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
15.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insertupdate 的效率,因为 insertupdate 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
16.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
18.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
19.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
21.避免频繁创建和删除临时表,以减少系统表资源的消耗。
22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
23.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
27.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
28.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
29.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
30.尽量避免大事务操作,提高系统并发能力。

4.总结

本章节主要是讲了单机优化,从规则,存储引擎,索引,SQL优化等方向去进行优化,如果还有其它好的优化方案可以评论区说出来哦,大家共同学习共同进步
文章内容是我学习的笔记,来源由网上等资料外加自我实战的结果,方便以后随时查阅!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值