常用的聚合函数有COUNT()、AVG()、SUM()、MAX()、MIN(),下面以MySQL为例,说明这些函数的作用。
COUNT()函数:
COUNT()函数统计数据表中包含的记录行的总数,或者根据查询结果返回列中包含的数据行数,它有两种用法:
● COUNT(*)计算表中总的行数,不管某列是否有数值或者为空值。
● COUNT(字段名)计算指定列下总的行数,计算时将忽略空值的行。
COUNT()函数可以与GROUP BY一起使用来计算每个分组的总和。
AVG()函数():
AVG()函数通过计算返回的行数和每一行数据的和,求得指定列数据的平均值。
AVG()函数可以与UP BY一起使用,来计算每个分组的平均值。
SUM()函数:
SUM()是一个求总和的函数,返回指定列值的总和。
SUM()可以与GROUP BY一起使用,来计算每个分组的总和。
MAX()函数:
MAX()返回指定列中的最大值。
MAX()也可以和GROUP BY关键字一起使用,求每个分组中的最大值。
MAX()函数不仅适用于查找数值类型,也可应用于字符类型。
MIN()函数:
MIN()返回查询列中的最小值。
MIN()也可以和GROUP BY关键字一起使用,求出每个分组中的最小值。
MIN()函数与MAX()函数类似,不仅适用于查找数值类型,也可应用于字符类型。
表与表之间常用的关联方式有两种:内连接、外连接,下面以MySQL为例来说明这两种连接方式。
update b set b.col=a.col from a,b where a.id =b.id;
update b set b.col=a.col from b inner join a on a.id=b.id;
update b set b.col=a.col from b left join a on a. id=b.id;
where是一个约束声明,使用where约束来自数据库的数据,where是在结果返回之前起作用的,where中不能使用聚合函数。
having是一个过滤声明,实在查询返回结果集之后对查询结果进行的过滤操作,在having中可以使用聚合函数。另一方面,having子句中不能使用除了分组字段和聚合函数之外的其他字段。
从性能的角度来说,HAVING子句中如果使用了分组字段作为过滤条件,应该替换成WHERE子句。因为WHERE可以在执行分组操作和计算聚合函数之前过滤掉不需要的数据,性能会更好。
索引是一个单独的、存储在磁盘上的数据库结构,包含着对数据表里所有数据记录的引用指针。使用索引可以快速找出在某个或者多个列中有一特定值的行, 对相关列使用索引是提高查询操作速度的最佳途径。
索引是在存储引擎中实现的,因此,每种存储引擎的索引都不一定完全相同,并且每种存储引擎也不一定支持所有的索引类型。Mysql中的存储类型有两种,即BTree和Hash,具体和表的存储引擎相关。MyISAM和InnoDB存储引擎只支持BTREE索引;Memory/HEAP 存储引擎可以支持HASH和BTREEA索引。
使用索引的优点:
使用索引的缺点:
● 普通索引是Mysql中的基本索引类型,允许在定义索引的列中插入重复值和空值。
● 唯一索引要求索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
● 主键索引是一种特殊的唯一索引,不允许有空值。
● 单列索引即一个索引只包含单个列,一个表可以有多个单列索引。
● 组合索引是在表的多个字段种组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用。使用组合索引时遵循最左前缀集合。
组合索引:
创建组合索引:ALTER TABLE
table_name
ADD INDEX (col1
,col2
,col3
);
当创建(col1,col2,col3)联合索引时,相当于创建了(col)单列索引,(clo1,clo2)联合索引以及(col1,col2,col3)联合索引想要索引生效,只能使用col1和col1、col2和col1、col2、col3三种组合;当然,col1,col3组合也可以,但实际上只用到了col1的索引,col3并没有用到!
关于更详细的组合索引 点击传送门:组合索引的使用
全文索引类型为FULLTEXT,在定义索引的列上支持值的全文查找,允许在这些列中插入重复值和空值。全文索引和在char、varchar和text类型的列上创建。
关于更详细的组合索引 点击传送门:全文索引的使用
空间索引是对空间数据类型的字段创建的索引,Mysql中空间数据类型有四种,分别是GEOMETRY(几何图形)、POINT(点)、LineString(图)和POLYGON(多边形)。创建空间索引的列,必须将其声明为NOT NULL,空间索引只能在存储引擎为MyISAM的表中创建。
建议按照如下的原则来进行创建索引:
● 当唯一性是某种数据本身的特征时,指定唯一索引。使用唯一需确保定义的列的数据完整性,以提高查询速度。
● 在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引,如果待排序的列有多个,可以在这些列上建立组合索引。
● 数据量小的表最好不要使用索引,由于数据较少,查询花费的时间可能比遍历索引的时间还要短,索引可能不会产生优化效果。
● 避免对经常更新的表进行过多的索引,并且索引中的列要尽可能少。应该经常用于查询的字段创建索引,但要避免添加不必要的字段。
以使用EXPLAIN语句查看索引是否正在使用。
举例,假设已经创建了book表,并已经在其year_publication字段上建立了普通索引。执行如下语句:
EXPLAIN SELECT * FROM book WHERE year_publication=1990;
EXPLAIN语句将为我们输出详细的SQL执行信息,其中:
● possible_keys行给出了MySQL在搜索数据记录时可选用的各个索引。
● key行是MySQL实际选用的索引。
如果possible_keys行和key行都包含year_publication字段,则说明在查询时使用了该索引。
● 索引分析传送门
● MyISAM和InnoDB的区别
在InnoDB存储引擎中,可以将B+树索引分为聚簇索引和辅助索引(非聚簇索引)。无论是何种索引,每个页的大小都为16KB,且不能更改。
聚簇索引是根据主键创建的一棵B+树,聚簇索引的叶子节点存放了表中的所有记录。辅助索引是根据索引键创建的一棵B+树,与聚簇索引不同的是,其叶子节点仅存放索引键值,以及该索引键值指向的主键。也就是说,如果通过辅助索引来查找数据,那么当找到辅助索引的叶子节点后,很有可能还需要根据主键值查找聚簇索引来得到数据,这种查找方式又被称为书签查找。因为辅助索引不包含行记录的所有数据,这就意味着每页可以存放更多的键值,因此其高度一般都要小于聚簇索引。
事务可由一条非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成。在事务中的操作,要么都执行修改,要么都不执行。这就是事务的目的。
A,原子性 原子性指整个数据库是不可分割的工作单位。只有使事务中所有的数据库操作都执行成功,整个事务的执行才算成功。事务中任何一个SQL语句执行失败,那么执行成功的语句也必须撤销,数据库状态应该回退到执行事务前的状态。
C,一致性:一致性之事务将数据库从一种状态转变为另一种一致的状态。在事务开始之前和事务结束之后,数据库的完整性约束没有被破坏。
I, 隔离性:事务的隔离性要求每个读写事务的对象与其他事务的对象能相互分离,即该事务提交前对其他事务都不可见,这通常使用锁来实现。
D,持久性:事务一旦提交,其结果就是永久性的,即使发生宕机等故障,数据库也能将数据回复。持久性保证的是事务系统的高可靠性,而不是高可用性。
扁平事务:是事务类型中最简单的一种类型,而在实际生产环境中,这可能是使用最为频繁的事务。在扁平事务中,所有操作都处于同一层次,其有begin work 开始,由commit work 或rollback work 结束。处于之间的操作是原子的,要饿都执行,要么都不执行。
带有保存点的扁平事务:除了支持扁平事务支持的操作外,允许在事务执行过程中回滚到同一事务中较早的一个状态,这是因为可能某些事务在执行过程中出现的错误并不会对所有的操作都无效,放弃整个事务不合乎要求,开销也太大。保存点(savepoint)用来通知系统应该记住事务当前的状态,以便以后发生错误时,事务能回到该状态。
链事务:可视为保存点模式的一个变种。链事务的思想是:在提交一个事务时,释放不需要的数据对象,将必要的处理上下文隐式地传给下一个要开始的事务。注意,提交事务操作和开始下一个事务操作将合并为一个原子操作。这意味着下一个事务将看到上一个事务的结果,就好像在一个事务中进行的。
嵌套事务:是一个层次结构框架。有一个顶层事务(top-level transaction)控制着各个层次的事务。顶层事务之下嵌套的事务被称为子事务(subtransaction),其控制每一个局部的变换。
分布式事务:通常是一个在分布式环境下运行的扁平事务,因此需要根据数据所在位置访问网络中的不同节点。对于分布式事务,同样需要满足ACID特性,要么都发生,要么都失效。
对于MySQL的InnoDB存储引擎来说,它支持扁平事务、带有保存点的扁平事务、链事务、分布式事务。对于嵌套事务,MySQL数据库并不是原生的,因此对于有并行事务需求的用户来说MySQL就无能为力了,但是用户可以通过带有保存点的事务来模拟串行的嵌套事务。
原子性实现原理 :实现原子性的关键,是当事务回滚时能够撤销所有已经成功执行的sql语句。InnoDB实现回滚靠的是undo log,当事务对数据库进行修改时,InnoDB会生成对应的undo log。如果事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。undo log属于逻辑日志,它记录的是sql执行相关的信息。
持久性实现原理 :InnoDB作为MySQL的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(Buffer Pool),作为访问数据库的缓冲。当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool。当向数据库写入数据时,会首先写入Buffer Pool。
Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
于是,redo log被引入来解决这个问题。当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作。当事务提交时,会调用fsync接口对redo log进行刷盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。
隔离性实现原理:
一致性实现原理:
可以说,一致性是事务追求的最终目标。前面提到的原子性、持久性和隔离性,都是为了保证数据库状态的一致性。此外,除了数据库层面的保障,一致性的实现也需要应用层面进行保障。实现一致性的措施包括:
● 保证原子性、持久性和隔离性,如果这些特性无法保证,事务的一致性也无法保证。
● 数据库本身提供保障,例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等。
● 应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现的多么完美,也无法保证状态的一致。
SQL 标准定义了以下四种隔离级别
● 读未提交(READ UNCOMMITTED):
● 读已提交(READ COMMITTED):
● 可重复读(REPEATABLE READ):
● 串行化(SERIALIZABLE):
事务隔离是为了解决脏读、不可重复读、幻读问题,下表展示了 4 种隔离级别对这三个问题的解决程度:
上述4种隔离级别MySQL都支持,并且InnoDB存储引擎默认的支持隔离级别是REPEATABLE READ,但是与标准SQL不同的是,InnoDB存储引擎在REPEATABLE READ事务隔离级别下,使用Next-Key Lock的锁算法,因此避免了幻读的产生。所以,InnoDB存储引擎在默认的事务隔离(可重复读)级别下已经能完全保证事务的隔离性要求,即达到SQL标准的SERIALIZABLE隔离级别。
扩展阅读 并发情况下,读操作可能存在的三类问题:
- 脏读:当前事务(A)中可以读到其他事务(B)未提交的数据(脏数据),这种现象是脏读。
- 不可重复读:在事务A中先后两次读取同一个数据,两次读取的结果不一样,这种现象称为不可重复读。脏读与不可重复读的区别在于:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据。
- 幻读:在事务A中按照某个条件先后两次查询数据库,两次查询结果的条数不同,这种现象称为幻读。不可重复读与幻读的区别可以通俗的理解为:前者是数据变了,后者是数据的行数变了。
锁是数据库系统区别于文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。
InnoDB存储引擎实现了如下两种标准的行级锁:
● 共享锁(S Lock),允许事务读一行数据。
● 排他锁(X Lock),允许事务删除或更新一行数据。
如果一个事务T1已经获得了某行的共享锁,那么另外的事务T2可以也获得这行的共享锁,因为读取并没有改变这一行的数据,称这种情况为锁兼容。但若有其他的事务T3想获得这行的排他锁,则其必须等待事务T1、T2释放这行上的共享锁,这种情况称为锁不兼容。下图显示了共享锁和排他锁的兼容性,可以发现X锁与任何的锁都不兼容,而S锁仅和S锁兼容。需要特别注意的是,S和X锁都是行锁,兼容是指对同一记录(row)锁的兼容性情况。
nnoDB存储引擎支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。
InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事务中揭示下一行将被请求的锁类型。其支持两种意向锁:
● 意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁。
● 意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁。
由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫以外的任何请求。故表级意向锁与行级锁的兼容性如下图所示。
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力作用,事务都将无法推进下去。
解决死锁问题最简单的一种方法是超时,即当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另一个等待的事务就能继续进行。
除了超时机制,当前数据库还都普遍采用wait-for graph(等待图)的方式来进行死锁检测。较之超时的解决方案,这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph要求数据库保存以下两种信息:
● 锁的信息链表;
● 事务等待链表;
InnoDB行级锁是通过给索引上的索引项加锁来实现的。只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。
当表中锁定其中的某几行时,不同的事务可以使用不同的索引锁定不同的行。另外,不论使用主键索引、唯一索引还是普通索引,InnoDB都会使用行锁来对数据加锁。
MySQL数据库优化是多方面的,原则上是减少系统的瓶颈,减少资源的占用,增加系统的相应速度。
参考问题8回答
优化子查询:
子查询虽然可以使查询语句很灵活,但执行效率不高。执行子查询时,MySQL需要为内层查询语句的查询结果建立一个临时表。然后外层查询语句从临时表中查询记录。查询完毕后,再撤销这些临时表。因此,子查询的速度会受到一定的影响。
在MySQL中,可以使用连接(JOIN)查询来替代子查询。连接查询不需要建立临时表,其速度比子查询要快,如果查询中使用索引,性能会更好。
影响插入速度的主要是索引、唯一性校验、一次插入记录条数等。针对这些情况,可以分别进行优化。
对于MyISAM引擎的表,常见的优化方法如下:
对于InnoDB引擎的表,常见的优化方法如下:
在大量的SQL脚本中,如何快速定位性能比较低、速度慢的SQL?可以通过慢查询日志来快速定位。慢查询日志是MySQL提供的一种特殊的记录日志,用来记录Mysql中响应时间超过阈值的SQL语句(可以通过long_query_time设置,默认10秒)
MySQL默认关闭了这个功能,我们可以在调试时临时开启,在生产环境下关闭该功能。
通过配置文件my.ini或者my.cnf中的log-slow-queries选项打开,也可以在MySQL服务启动的时候使用–log-slow-queries[=file_name]启动慢查询日志。
启动慢查询日志时,需要在my.ini或者my.cnf文件中配置long_query_time选项指定记录阈值,如果某条查询语句的查询时间超过了这个值,这个查询过程将被记录到慢查询日志文件中。
分析慢查询日志:
直接分析mysql慢查询日志,利用explain关键字可以模拟优化器执行SQL查询语句,来分析sql慢查询语句。
MySQL中提供了EXPLAIN语句和DESCRIBE语句,用来分析查询语句,EXPLAIN语句的基本语法如下:
EXPLAIN [EXTENDED] SELECT select_options
可以分析EXPLAIN后面SELECT语句的执行情况,并且能够分析出所查询表的一些特征。下面对查询结果进行解释:
● id:SELECT识别符。这是SELECT的查询序列号。
● select_type:表示SELECT语句的类型。
● table:表示查询的表。
● type:表示表的连接类型。
● possible_keys:给出了MySQL在搜索数据记录时可选用的各个索引。
● key:是MySQL实际选用的索引。
● key_len:给出索引按字节计算的长度,key_len数值越小,表示越快。
● ref:给出了关联关系中另一个数据表里的数据列名。
● rows:是MySQL在执行这个查询时预计会从这个数据表里读出的数据行的个数。
● Extra:提供了与关联操作有关的信息。
explain关注什么?
1NF: 字段是最小的的单元不可再分。
2NF:满足 1NF,表中的字段必须完全依赖于全部主键而非部分主键(一般我们都会做到)。
3NF:满足 2NF,非主键外的所有字段必须互不依赖。
binlog(Binary Log):
二进制日志文件就是常说的binlog。二进制日志记录了MySQL所有修改数据库的操作,然后以二进制的形式记录在日志文件中,其中还包括每条语句所执行的时间和所消耗的资源,以及相关的事务信息。
默认情况下,二进制日志功能是开启的,启动时可以重新配置–log-bin[=file_name]选项,修改二进制日志存放的目录和文件名称。
redo log:
重做日志用来实现事务的持久性,即事务ACID中的D。它由两部分组成:一是内存中的重做日志缓冲(redo log buffer),其是易失的;二是重做日志文件(redo log file),它是持久的。
InnoDB是事务的存储引擎,当事务提交(COMMIT)时,必须先将该事务的所有日志写入到重做日志文件进行持久化,待事务的COMMIT操作完成才算完成。这里的日志是指重做日志,在InnoDB存储引擎中,由两部分组成,即redo log和undo log。
redo log用来保证事务的持久性,undo log用来帮助事务回滚及MVCC的功能。redo log基本上都是顺序写的,在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读写的。
undo log:
重做日志记录了事务的行为,可以很好地通过其对页进行“重做”操作。但是事务有时还需要进行回滚操作,这时就需要undo。因此在对数据库进行修改时,InnoDB存储引擎不但会产生redo,还会产生一定量的undo。这样如果用户执行的事务或语句由于某种原因失败了,又或者用户用一条ROLLBACK语句请求回滚,就可以利用这些undo信息将数据回滚到修改之前的样子。
特性区别如下:
当存储过程和函数被执行的时候,SQL Manager会到procedure cache中去取相应的查询语句,如果在procedure cache里没有相应的查询语句,SQL Manager就会对存储过程和函数进行编译。
Procedure cache中保存的是执行计划 (execution plan) ,当编译好之后就执行procedure cache中的execution plan,之后SQL SERVER会根据每个execution plan的实际情况来考虑是否要在cache中保存这个plan,评判的标准一个是这个execution plan可能被使用的频率;其次是生成这个plan的代价,也就是编译的耗时。保存在cache中的plan在下次执行时就不用再编译了。