(一)数据库底层
转自:https://blog.csdn.net/m0_38075425/article/details/82256315
仅供个人学习使用
数据库(DataBase)是存放用户数据的地方,当用户访问、操作数据库中的数据时,需要数据库管理系统的帮助。数据管理系统的全称是DataBase Management System,简称DBMS。通常情况下我们会把数据库和数据库管理系统笼统的称为数据库,通常所说的数据库既包括存储用户数据的部分,也包括管理数据库的管理系统。
MySQL是一种关系型数据库管理系统,关系型数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件。MySQL使用 C和 C++编写,其体积小、速度快、源码开放,如今被应用在大量的中小型应用上。
1.MySQL架构
上图为MySQL的逻辑架构图:
支持接口是第三方语言对数据库的操作接口,这里不再赘述。
1)连接层
最上层的连接池是一些连接服务,包含本地sock通信和大多数基于C/S工具实现的类似于TCP/IP的通信。主要完成一些类似于连接处理、授权认证及相关的安全方案。在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全连接。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
2)服务层
第二层架构主要完成大多数的核心服务功能,如SQL接口、缓存的查询、SQL的分析和优化、内置函数等。所有跨存储引擎的功能也在这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定查询表的顺序,是否利用索引等,最后生成相应的执行操作。如果是select语句,服务器还会查询内部的缓存,如果缓存空间足够大,这样在频繁读操作的环境中能够很好的提升系统的性能。
3)引擎层
存储引擎真正的负责MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信,不同的存储引擎具有的特性不同,我们可以根据实际需进行选取。下文将对相关存储引擎进行具体介绍。
4)存储层
数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。
SQL的执行过程:数据库通常不会被单独使用,而是由其它编程语言通过SQL支持接口调用MySQL,由MySQL处理并返回执行结果。首先,其它编程语言通过SQL支持接口调用MySQL,MySQL收到请求后,会将该请求暂时放在连接池,并由管理服务与工具进行管理。当该请求从等待队列进入到处理队列时,管理器会将该请求传给SQL接口,SQL接口接收到请求后,它会将请求进行hash处理并与缓存中的数据进行对比,如果匹配则通过缓存直接返回处理结果;否则,去文件系统查询:由SQL接口传给后面的解析器,解析器会判断SQL语句是否正确,若正确则将其转化为数据结构。解析器处理完毕后,便将处理后的请求传给优化器控制器,它会产生多种执行计划,最终数据库会选择最优的方案去执行。确定最优执行计划后,SQL语句交由存储引擎处理,存储引擎将会到文件系统中取得相应的数据,并原路返回。
2.MySQL的存储引擎
MySQL在5.1版本之前默认存储引擎为MyISAM,在此版本之后为InnoDB。
1)MyISAM存储引擎
MyIsam 的存储文件有三个,后缀名分别是 .frm、.MYD、MYI,其中 .frm 是表的定义文件,.MYD 是数据文件,.MYI 是索引文件。MyIsam 只支持表锁,不支持事务。MyIsam 由于有单独的索引文件,在读取数据方面的性能很高。Myisam是以堆结构进行组织数据,其表容易损坏。
2)InnoDB
InnoDB 的存储文件有两个,后缀名分别是 .frm 和 .idb,其中 .frm 是表的定义文件,而 idb 是数据文件。InnoDB 中存在表锁和行锁,不过行锁是在命中索引的情况下才会起作用。InnoDB 支持事务,且支持四种隔离级别(读未提交、读已提交、可重复读、串行化),默认的为可重复读。
两种存储引擎的对比:从MyISAM和InnoDB的存储文件可看出,MyISAM注重的是对数据的快速读取,但由于MyISAM不支持事务,同时缺乏灵活性。而InnoDB支持事务和行级锁,因此在5.1之后MySQL的默认存储引擎为InnoDB。
3)两大存储引擎的数据结构
MyISAM和InnoDB两种存储引擎都采用了B+树(有关B+树的概念和由来,请见笔者相关博客)的数据结构,但具体实现方式完全不同。
MyISAM的B+树实现示例图:
MyISAM的B+树叶子节点存储的并不是数据,而是数据地址,因此也称为非聚集索引,搜索按照B+树的搜索算法进行。
InnnDB的B+树实现示例图:
InnoDB的B+树叶子节点存储的就是数据本身,因此也称为聚集索引。从实现数据结构可知,如采用InnoDB存储引擎,不宜设置过长的主键。另外,使用InnoDB尽量采用自增(自减)的数据为主键,否则,由于叶子结点处直接存储数据的原因(数据即为索引),会造成频繁的B+的分裂合并调整,效率十分低下。从数据结构的实现看出,当读写更新比较频繁,读写一致性要求很高的业务,采用InnoDB更佳。
(二)数据库调优
一.创建索引
1.要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
2.在经常需要进行检索的字段上创建索引,一个表的索引数最好不要超过6个,索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
二.避免在索引上使用计算
在where字句中,如果索引列是计算或者函数的一部分,DBMS的优化器将不会使用索引而使用全表查询
三.使用预编译查询
程序中通常是根据用户的输入来动态执行SQL,这时应该尽量使用参数化SQL,这样不仅可以避免SQL注入漏洞攻击,最重要数据库会对这些参数化SQL进行预编译,这样第一次执行的时候DBMS会为这个SQL语句进行查询优化并且执行预编译,这样以后再执行这个SQL的时候就直接使用预编译的结果,这样可以大大提高执行的速度。
四.调整Where字句中的连接顺序
DBMS一般采用自下而上的顺序解析where字句,根据这个原理表连接最好写在其他where条件之前,那些可以过滤掉最大数量记录。
五.尽量将多条SQL语句压缩到一句SQL中
每次执行SQL的时候都要建立网络连接、进行权限校验、进行SQL语句的查询优化、发送执行结果,这个过程是非常耗时的,因此应该尽量避免过多的执行SQL语句,能够压缩到一句SQL执行的语句就不要用多条来执行。
六.用where字句替换HAVING字句
避免使用HAVING字句,因为HAVING只会在检索出所有记录之后才对结果集进行过滤,而where则是在聚合前刷选记录,如果能通过where字句限制记录的数目,那就能减少这方面的开销。HAVING中的条件一般用于聚合函数的过滤,除此之外,应该将条件写在where字句中。
七.使用表的别名
当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个列名上。这样就可以减少解析的时间并减少哪些有列名歧义引起的语法错误。
八.用union all替换union
当SQL语句需要union两个查询结果集合时,即使检索结果中不会有重复的记录,如果使用union这两个结果集同样会尝试进行合并,然后在输出最终结果前进行排序,因此如果可以判断检索结果中不会有重复的记录时候,应该用union all,这样效率就会因此得到提高。
九.考虑使用“临时表”暂存中间结果
简化SQL语句的重要方法就是采用临时表暂存中间结果,但是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在tempdb中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能。但是也得避免频繁创建和删除临时表,以减少系统表资源的消耗。
十.只在必要的情况下才使用事务begin translation
SQL Server中一句SQL语句默认就是一个事务,在该语句执行完成后也是默认commit的。其实,这就是begin tran的一个最小化的形式,好比在每句语句开头隐含了一个begin tran,结束时隐含了一个commit。有些情况下,我们需要显式声明begin tran,比如做“插、删、改”操作需要同时修改多个表,要求要么几个表都修改成功,要么都不成功。begin tran 可以起到这样的作用,它可以把若干SQL语句套在一起执行,最后再一起commit。 好处是保证了数据的一致性,但任何事情都不是完美无缺的。Begin tran付出的代价是在提交之前,所有SQL语句锁住的资源都不能释放,直到commit掉。可见,如果Begin tran套住的SQL语句太多,那数据库的性能就糟糕了。在该大事务提交之前,必然会阻塞别的语句,造成block很多。Begin tran使用的原则是,在保证数据一致性的前提下,begin tran 套住的SQL语句越少越好!有些情况下可以采用触发器同步数据,不一定要用begin tran。
十一.尽量避免使用游标
尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
十二.用varchar/nvarchar 代替 char/nchar
尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
十三.查询select语句优化
1.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
十四.更新Update语句优化
如果只更改1、2个字段,不要Update全部字段,否则频繁调用会引起明显的性能消耗,同时带来大量日志
十五. 删除Delete语句优化语句
使用ROWID
十六.插入Insert语句优化
在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
总结:
优化方向主要有:
1.索引:在常用列上加入索引(避免全局扫描)、避免在索引列上做计算
2.sql语句:尽量一条sql、where后面先跟表连接、多where少having、尽量select只查询需要的数据、表别名
3.预编译查询、临时表、减少使用事务、避免生成日志