一)数据库的优化
【单机优化】
1、优化表结构设计,例如合理选择表字段类型,3NF和反3NF,(可能会有“逆规范化”的情况)
2、使用索引(查询速度提高 但以插入、更新、删除的速度为代价)
3、Sql语句的优化:
(1)在表中建立索引,优先考虑where、group by使用到的字段。
(2)尽量避免使用select *,返回无用的字段会降低查询效率。
(3)尽量避免使用in 和not in,会导致数据库引擎放弃索引进行全表扫描。
(4)尽量避免使用or,会导致数据库引擎放弃索引进行全表扫描。
(5)尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。
(6)尽量避免进行null值的判断,会导致数据库引擎放弃索引进行全表扫描。
(7)尽量避免在where条件中等号的左侧进行表达式、函数操作,会导致数据库引擎放弃索引进行全表扫描。
(8)当数据量大时,避免使用where 1=1的条件。
4、分表:水平分表、垂直分表
(1)水平分表:根据一列或多列数据的值将数据行放在不同表中
(2)垂直分表:如果一张表某 个字段,信息量大且不经常查询,则可以考虑把这些字段,单独的放入到另一张表中,用外键关联。如果硬是要查询,就是用跨表查询(join)。
5、分区:不同在于分表将大表分解为若干个独立的实体表,而分区是将数据分段划分在多个位置存放,可以是同一块磁盘也可以在不同的机器。分区后,表面上还是一张表,但数据散列到多个位置了。app读写的时候操作的还是大表名字,db自动去组织分区的数据。
【多机优化】
1、主从复制+读写分离+主备切换(集群):
主从复制:在主服务器中配置生成二进制文件,并授权给一个账户,从服务器拿到该账户去访问二进制文件,还原成自己的日志,再还原成数据。
读写分离:通过mysql_proxy来代理管理主从数据库服务器,并对外暴露一个虚拟的端口(主从服务器中应该有同一个用户)。
主备切换:防止单点故障。
2、分布式
按业务分要访问哪一个集群,即将不同模块的业务分到不同数据库集群上面。
二)索引的种类及其作用
(1)常规索引,也叫普通索引(index或key),它可以常规地提高查询效率。一张数据表中可以有多个常规索引。常规索引是使用最普遍的索引类型,如果没有明确指明索引的类型,我们所说的索引都是指常规索引。
alter table table_name add index (字段名
);
(2)主键索引(Primary Key),也简称主键。它可以提高查询效率,并提供唯一性约束。一张表中只能有一个主键。被标志为自动增长的字段一定是主键,但主键不一定是自动增长。一般把主键定义在无意义的字段上(如:编号),主键的数据类型最好是数值。
alert table tablename add primary key (字段名
)
(3)唯一索引(Unique Key),可以提高查询效率,并提供唯一性约束。一张表中可以有多个唯一索引。
alter table table_name add primary key (字段名
);
(4)全文索引(Full Text),可以提高全文搜索的查询效率,一般使用Sphinx替代。但Sphinx不支持中文检索,Coreseek是支持中文的全文检索引擎,也称作具有中文分词功能的Sphinx。实际项目中,我们用到的是Coreseek。
alter table 表名 add FULLTEXT(字段名
);
(5)外键索引(Foreign Key),简称外键,它可以提高查询效率,外键会自动和对应的其他表的主键关联。外键的主要作用是保证记录的一致性和完整性。
foreign key(cate_id) references cms_cate(id)
注意:只有InnoDB存储引擎的表才支持外键。外键字段如果没有指定索引名称,会自动生成。如果要删除父表(如分类表)中的记录,必须先删除子表(带外键的表,如文章表)中的相应记录,否则会出错。创建表的时候,可以给字段设置外键,由于外键的效率并不是很好,因此并不推荐使用外键,但我们要使用外键的思想来保证数据的一致性和完整性。
三)Mysql索引的底层为什么使用B+树而不使用B树?
1、B+TREE是B TREE的变种,B TREE能解决的问题,B+TREE也能够解决(降低树的高度,增大节点存储数据量)
2、 B+TREE扫库和扫表能力更强,如果我们要根据索引去进行数据表的扫描,对B TREE进行扫描,需要把整棵树遍历一遍,而B+TREE只需要遍历它的所有叶子节点即可(叶子节点之间有引用)。
3、B+TREE磁盘读写能力更强,他的根节点和支节点不保存数据区,所有根节点和支节点同样大小的情况下,保存的关键字要比B TREE要多。而叶子节点不保存子节点引用。所以,B+TREE读写一次磁盘加载的关键字比B TREE更多。
4、B+TREE排序能力更强,如上面的图中可以看出,B+TREE天然具有排序功能。
5、B+TREE查询效率更加稳定,每次查询数据,查询IO次数一定是稳定的。
四)执行引擎InnoDB、MyISAM
【InnoDB】
InnoDB是默认的事务型存储引擎,也是最重要,使用最广泛的存储引擎。在没有特殊情况下,一般优先使用InnoDB存储引擎。
(1)数据存储形式:使用InnoDB时,会将数据表分为.frm 和 ibd两个文件进行存储。.frm存储表结构.ibd存储表的数据和索引
(2)锁的粒度:InnoDB采用MVCC(多版本并发控制)来支持高并发,InnoDB实现了四个隔离级别,默认级别是REPETABLE READ,并通过间隙锁策略防止幻读的出现。它的锁粒度是行锁。
(3)事务:InnoDB是典型的事务型存储引擎,并且通过一些机制和工具,支持真正的热备份。
(4)数据的存储特点:InnoDB表是基于聚簇索引(另一篇博客有介绍)建立的,聚簇索引对主键的查询有很高的性能,不过他的二级索引(非主键索引)必须包含主键列,索引其他的索引会很大。
【MyISAM】
(1)数据存储形式:MyISAM采用的是索引与数据分离的形式,将数据保存在三个文件中.frm存储表结构.MYD存储表数据,.MYIs存储表索引。
(2)锁的粒度:MyISAM不支持行锁,所以读取时对表加上共享锁,在写入是对表加上排他锁。由于是对整张表加锁,相比InnoDB,在并发写入时效率很低。
(3)事务:MyISAM不支持事务。
(4)数据的存储特点:MyISAM是基于非聚簇索引进行存储的。
(5)其他:MyISAM提供了大量的特性,包括全文索引,压缩,空间函数,延迟更新索引键等。
进行压缩后的表是不能进行修改的,但是压缩表可以极大减少磁盘占用空间,因此也可以减少磁盘IO,从而提供查询性能。
全文索引,是一种基于分词创建的索引,可以支持复杂的查询。
延迟更新索引键,不会将更新的索引数据立即写入到磁盘,而是会写到内存中的缓冲区中,只有在清除缓冲区时候才会将对应的索引写入磁盘,这种方式大大提升了写入性能。
【对比与选择】
两种存储引擎各有各的有点,MyISAM专注性能,InnoDB专注事务。两者最大的区别就是InnoDB支持事务和行锁。
如何在两种存储引擎中进行选择?
① 是否有事务操作?有,InnoDB。
②是否存储并发修改?有,InnoDB。
③是否追求快速查询,且数据修改较少?是,MyISAM。
④是否使用全文索引?如果不引用第三方框架,可以选择MyISAM,但是可以选用第三方框架和InnDB效率会更高。
五)****行级锁****、****表级锁****。****悲观锁****、****乐观锁****。
【悲观锁】
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。
【乐观锁】
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。
更新语句设置版本号,在指定版本中更新数据
一方:
update account set money=money-200,version=version+1 where id=1 and version=0;
另一方操作同一个版本号,则不能更新数据
例如另一方:update account set money=money+200,version=version+1 where id=1 and version=0;
如果更新多,查询少,用悲观锁;反之,乐观锁
【行级锁】
行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁 和排他锁。
行级锁,一般是指排它锁,即被锁定行不可进行修改,删除,只可以被其他会话select。行级锁之前需要先加表结构共享锁。
行级锁,where使用主键。
特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
【表级锁】
表级锁是MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。
表级锁,一般是指表结构共享锁,是不可对该表执行DDL操作,但对DML操作都不限制。
表级锁,where用的是非主键。
特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。
【使用】
如果用表级锁,其他用户将不能进行查询操作,因此开发中用行级锁。
以mysql为例,有索引并且使用了该索引当条件的时候就是行锁,没有索引的时候就是表锁。innodb 的行锁是在有索引的情况下,没有索引的表是锁定全表的。
行级锁之前需要先加表结构共享锁。
六)编写Sql
sql链表有两种方式
一是join on + 连接条件 称为sql99 意思是99年才出现的
二是连接条件放在where后面,是一种比较过时的连接技巧
七)存储过程、存储函数
【存储过程】:
1、概述:存储过程是存储在数据库目录中的一坨的声明性SQL语句。
2、优势:减少传递冗余过长的sql语句,效率相对较高,编译后的存储过程会放入mysql缓存中。
劣势:占用内存,不利于维护,较难调试。
3、创建存储过程:
CREATE PROCEDURE p1()
BEGIN
SELECT * FROM exam.yhb;
END
调用该存储过程
CALL p1()
4、变量:
declare idd int default 0;
Set idd = 10;
5、参数
IN 输入参数:表示调用者向过程传入值(传入值可以是字面量或变量)
OUT 输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)
INOUT 输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)
【存储函数】:
存储函数与存储过程本质上是一样的,都是封装一系列SQL语句,简化调用。
劣势:只能返回一条结果记录。存储函数只能指明一列数据作为结果,而存储过程能够指明多列数据作为结果。
函数(function)为一命名的存储程序,可带参数,并返回一计算值。函数和过程的结构类似,但必须有一个return子句,用于返回函数值。函数说明要指定函数名、结果值的类型,以及参数类型等。
存储函数语法:
create[or replace] functiion
函数名(参数列表)
return函数值类型
as
PLSQL子程序体
如果只有一个返回值,用存储函数;否则,就用存储过程。
八)plsql
PL/SQL是Oracle数据库对SQL语句的扩展。在普通SQL语句的使用上增加了编程语言的特点,所以PL/SQL把数据操作和查询语句组织在PL/SQL代码的过程性单元中,通过逻辑判断、循环等操作实现复杂的功能或者计算。MySQL 不支持 PL/SQL ,但支持Navicat Premium。
PL/SQL程序都是以块(block)为基本单位,整个PL/SQL块分三部分:声明部分(用declare开头)、执行部分(以 begin开头)和异常处理部分(以exception开头)。其中执行部分是必须的,其他两个部分可选。无论PL/SQL程序段的代码量有多大,其基本结构就是由这三部分组成。
模板如下:
Declare
/* 声明区(可选):定义类型和变量、声明变量、声明函数、游标 */
Begin
/* 执行区(必须的):执行pl/sql语句或者sql语句 */
Exception
/* 异常处理区(可选):处理错误的 */
end;
九)触发器
1、概念:
触发器(trigger)是MySQL提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作(insert,delete, update)时就会激活它执行。
触发器分为行触发器和语句触发器,Mysql不支持语句触发器。
2、触发器创建的四个要素
(1)监视地点(table)
(2)监视事件(insert/update/delete)
(3)触发时间(after/before)
(4)触发事件(insert/update/delete)
3、语法:
CREATE TRIGGER trigger_name
trigger_time
trigger_event ON tbl_name
FOR EACH ROW
trigger_stmt
参数说明:
trigger_name:标识触发器名称,用户自行指定;
trigger_time:标识触发时机,取值为 BEFORE 或 AFTER;
trigger_event:标识触发事件,取值为 INSERT、UPDATE 或 DELETE;
tbl_name:标识建立触发器的表名,即在哪张表上建立触发器;
trigger_stmt:触发器程序体,可以是一句SQL语句,或者用 BEGIN 和 END 包含的多条语句。
可以建立6种触发器,即:BEFORE INSERT、BEFORE UPDATE、BEFORE DELETE、AFTER INSERT、AFTER UPDATE、AFTER DELETE。
限制:不能同时在一个表上建立2个相同类型的触发器,因此在一个表上最多建立6个触发器。
4、查看和删除已有的触发器
(1)查看已有触发器:show triggers
(2)删除已有触发器:drop trigger triggerName
5、示例:
在下订单的时候,对应的商品的库存量要相应的减少,即买几个商品就减少多少个库存量。
订单表:ord
商品表:goods
(1)首先来创建表并添加几条数据:
|
create table goods(
gid int,
name varchar(20),
num smallint
);
create table ord(
oid int,
gid int,
much smallint
);
insert into goods values(1,'cat',40);
insert into goods values(2,'dog',63);
insert into goods values(3,'pig',87);
|
(2)然后按照触发器创建的四个要素来进行分析:
监视谁:ord(订单表)
监视动作:insert(插入操作)
触发时间:after(在插入操作后触发)
触发事件:update(触发更新操作)
(3)最后创建触发器:
|
create trigger t1
after
insert
on ord
for each row
begin
update goods set num=num-1 where gid = 1;
end;
|
6、触发器中引用行变量
(1)在触发目标上执行insert操作后会有一个新行,如果在触发事件中需要用到这个新行的变量,可以用new关键字表示
(2)在触发目标上执行delete操作后会有一个旧行,如果在触发事件中需要用到这个旧行的变量,可以用old关键字表示
(3)在触发目标上执行update操作后原纪录是旧行,新记录是新行,可以使用new和old关键字来分别操作
示例:
当下订单时减少相应的货品的库存量,创建触发器:
|
create trigger t2
after
insert
on ord
for each row
begin
update goods set num=num-new.much where gid=new.gid;
end;
|
当删除订单时增加相应的修改货品的库存量,创建触发器:
|
create trigger t3
after
delete
on ord
for each row
begin
update goods set num=num+old.much where gid=old.gid;
end;
|
当更新订单的购买数修改相应的修改货品的库存量,创建触发器:
|
create trigger t4
before
update
on ord
for each row
begin
update goods set num=num+old.much-new.much where gid = new.gid;
end;
|
7、after和before的区别
after操作,是在执行了监视动作后,才会执行触发事件;
before操作,是在执行了监视动作前,会执行触发事件。
十)视图
1、定义:
视图是是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表,并且在引用视图时动态生成。也即视图本身并不包含任何数据,它只包含映射到基表的一个查询语句,当基表数据发生变化,视图数据也随之变化。
使用视图时,将其当作表进行操作即可,由于视图是虚拟表,所以无法使用其对真实表进行创建、更新和删除操作,仅能做查询用。
2、操作:
(1)创建视图:create view 视图名称 as 普通的查询语句
(2)查询视图 select * from 视图名称;
(3)删除视图 drop view 视图名称;
3、功能:
(1)安全:有的数据是需要保密的,如果直接把表给出来进行操作会造成泄密,那么可以通过创建视图把相应视图的权限给出来即可保证数据的安全。
(2)高效:复杂的连接查询,每次执行时效率比较低,可以考虑新建视图,每次从视图中获取,将会提高效率。
(3)定制数据:将常用的字段放置在视图中。