从图上可以看到基本上每种设备都有两个指标:
延时(响应时间):表示硬件的突发处理能力;
带宽(吞吐量):代表硬件持续处理能力。
从上图可以看出,计算机系统硬件性能从高到代依次为:
CPU——Cache(L1-L2-L3)——内存——SSD硬盘——网络——硬盘
由于SSD硬盘还处于快速发展阶段,所以本文的内容不涉及SSD相关应用系统。
根据数据库知识,我们可以列出每种硬件主要的工作内容:
CPU及内存:缓存数据访问、比较、排序、事务检测、SQL解析、函数或逻辑运算;
网络:结果数据传输、SQL请求、远程数据库访问(dblink);
硬盘:数据访问、数据写入、日志记录、大数据量排序、大表连接。
数据库访问优化漏斗法则
由于每一层优化法则都是解决其对应硬件的性能问题,所以带来的性能提升比例也不一样。传统数据库系统设计是也是尽可能对低速设备提供优化方法,因此针对低速设备问题的可优化手段也更多,优化成本也更低。我们任何一个SQL的性能优化都应该按这个规则由上到下来诊断问题并提出解决方案,而不应该首先想到的是增加资源解决问题。
以下是每个优化法则层级对应优化效果及成本参考:
主要总结以下两者优化方式:
一.硬件优化(有钱出钱)
能用钱解决的事可以优先用钱解决
二.应用优化(没钱出力)
但我们没钱,所以主要关注在这里
一、硬件优化
.优化服务器硬件
服务器的硬件性能直接决定着数据库的性能,硬件的性能瓶颈,直接决定数据库的运行速度和效率。
可从以下几个方面考虑:
1 、配置较大的内存。足够大的内存,是提高数据库性能的方法之一。
内存的IO比硬盘快的多,可以增加系统的缓冲区容量,使数据在内存停留的时间更长,以减少磁盘的IO;
2 、配置高速磁盘,比如 SSD 。
3 、使用专业的存储服务器( NAS 、 SAN )
4 、合理分配磁盘IO,把磁盘IO分散到多个设备上,以减少资源的竞争,提高并行操作能力。
5 、配置多核处理器, 数据库一般是多线程的,多处理器可以提高同时执行多个线程的能力。
.优化服务器配置
合理配置数据库服务器,尽量使应用本身达到一个最合理的使用
.分配更多资源
配置更多的服务器等资源
二、应用优化
优化方式
1.设计合理的数据库结构
2.对数据表建立合适有效的数据库索引
3.交互优化
4.编写简洁高效规范的SOL语句
数据库访问优化法则详解:
数据库设计需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容
. 分库、分区
关系型数据库本身比较容易成为系统瓶颈,单机存储容量、连接数、处理能力都有限。
当数据量过大,查询维度较多,面临性能和存储的瓶颈时,考虑分库分区、读写分离等方式。
图,表现形式,多业务多库
.分表
将字段很多的表分解成多个表
对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。
因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。
表现形式:如扩展表
.增加中间表
对于需要经常联合查询的表,可以建立中间表以提高查询效率。
通过建立中间表,将需要通过联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询。
.合理增加冗余字段
设计数据表时应尽量遵循范式理论的规约,尽可能的减少冗余字段,让数据库设计看起来精致、优雅。
但是,合理的加入冗余字段可以提高查询速度。
表的规范化程度越高,表和表之间的关系越多,需要连接查询的情况也就越多,性能也就越差。
.考虑使用相应缓存服务帮助缓解访问压力
分库分区分表冗余冗余字段的使用,需注意保持同步更新,确保数据一致性。
方案:
1.编写统一更新方法,做好备注说明,相关业务统一调用更新同步数据
2.预备数据修复脚本,做好备注说明,以便数据异常时快速修复
表结构设计原则
选择合适的数据类型:如果能够定长尽量定长.
不要使用无法加索引的类型作为关键字段,比如 text 类型.
为了避免联表查询,可以适当的数据冗余.
选择合适的表引擎
建立合适的数据库索引
什么字段上建索引?
1、字段出现在查询条件中,并且查询条件可以使用索引;
2、语句执行频率高,一天会有几千次以上;
3.对数据表建立合适有效的数据库索引
索引:利用字段的某些属性,快速的定位数据(磁盘,柱面,磁道,扇区)
索引就是类似书的目录,很多时候能很大提高检索数据的效率。
索引创建一般原则 (主要是针对 where 条件进行优化)
.频繁的作为查询条件字段应该创建索引
select * from emp where empno = 1
.唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件
select * from emp where sex =‘男’
.更新非常频繁的字段不适合创建索引
select * from emp where logincount = 1
.不会出现在 WHERE子句中字段不该创建索引
.尽量使用短索引,一般对 int 、 char / varcbar 、 dateltime 等类型的字段建立索引
.单索引(如主键、外键等)与联合索引(常联合查询的条件)配合使用
.对大数据量表建立聚集索引,避免更新操作带来的碎片。
.不要过度索引,单表建立的索引不要超过 5 个,否则更新索引将很耗时
索引使用注意事项
避免对索引字段进行计算操作
避免在索引字段上使用not,<>,!=
避免在索引列上使用IS NULL和IS NOT NULL
避免在索引列上出现数据类型转换
避免在索引字段上使用函数 如LEFT() ,SUBSTR()等
避免建立索引的列中使用空值
.这样就可以直接只通过访问索引就能得到数据,一般索引占用的磁盘空间比表小很多,所以这种方式可以大大减少磁盘IO开销,快速定位。
.实际数据库中我们不可能把每个SQL请求的字段都建在索引里,所以这种只通过索引访问数据的方法一般只用于核心应用,也就是那种对核心表访问量最高且查询字段数据量很少的查询。
3.1.1、客户端(应用程序或浏览器)分页
将数据从应用服务器全部下载到本地应用程序或浏览器,在应用程序或浏览器内部通过本地代码进行分页处理
优点:编码简单,减少客户端与应用服务器网络交互次数
缺点:首次交互时间长,占用客户端内存
适应场景:客户端与应用服务器网络延时较大,但要求后续操作流畅
3.1.2、数据库SQL分页
采用数据库SQL分页需要两次SQL完成
一个SQL计算总数量
一个SQL返回分页后的数据
优点:性能好
缺点:编码复杂,各种数据库语法不同,需要两次SQL交互。
通过去除不必要的返回字段可以提高性能,例:
调整前:SELECT * FROM CF_User where UserID=?;
调整后:SELECT UserID,LoginName FROM CF_User where UserID=?;
优点:
1、减少数据在网络上传输开销
2、减少服务器数据处理开销
3、减少客户端内存占用
4、字段变更时提前发现问题,减少程序BUG
5、如果访问的所有字段刚好在一个索引里面,则可以使用纯索引访问提高性能。
缺点:增加编码工作量
Eg:用户实体
list
如果需要通过角色ID查数据可以这样:
List
通过用户Id查询
List
注意:减少交互次数的基础上,需要根据业务需求 来进行刷选需要的数据、避免每次都是直接查询所有;
优点:
.效率要好,因为是已经编译过的,sql要重新编译;
.处理复杂的报表统计的时候,使用存储过程,便于提高效率,以及业务逻辑的处理
缺点:
.不可移植性,每种数据库的内部编程语法都不太相同,当你的系统需要兼容多种数据库时最好不要用存储过程。
. 熟悉写存储过程,否则后期系统维护会产生问题 [重要的是 注意注释清楚用途]
.要通过优化业务逻辑来提高性能是比较困难的,这需要程序员对所访问的数据及业务流程非常清楚。
采用batch操作一般不会减少很多数据库服务器的物理IO,但是会大大减少客户端与服务端的交互次数,
从而减少了多次发起的网络延时开销,同时也会降低数据库的CPU开销。(尤其避免在循环里请求数据库连接)
1、防止SQL注入
2、提高SQL可读性
3、提高SQL解析性能
(绑定变量,如果判断有相同的SQL字符,并且确认是同一逻辑的SQL语句,则从共享池缓存中取出SQL对应的执行计划,根据执行计划读取数据并返回结果给客户端;无需再重新生成一个执行计划,然后根据执行计划读取数据并返回结果给客户端。)
示例:
详细说明【如下】
当一条SQL发送给数据库服务器后,系统首先会将SQL字符串进行hash运算,得到hash值后再从服务器内存里的SQL缓存区中进行检索,如果有相同的SQL字符,并且确认是同一逻辑的SQL语句,则从共享池缓存中取出SQL对应的执行计划,根据执行计划读取数据并返回结果给客户端。
如果在共享池中未发现相同的SQL则根据SQL逻辑生成一条新的执行计划并保存在SQL缓存区中,然后根据执行计划读取数据并返回结果给客户端。
为了更快的检索SQL是否在缓存区中,首先进行的是SQL字符串hash值对比,如果未找到则认为没有缓存,如果存在再进行下一步的准确对比,所以要命中SQL缓存区应保证SQL字符是完全一致,中间有大小写或空格都会认为是不同的SQL。
如果我们不采用绑定变量,采用字符串拼接的模式生成SQL,那么每条SQL都会产生执行计划,这样会导致共享池耗尽,缓存命中率也很低。
sql查询语句的处理步骤,流程图
参考:SQL执行一般顺序
(8)SELECT (9)DISTINCT (11)
(1)FROM [left_table]
(3)
(2)ON
(4)WHERE
(5)GROUP BY
(6)WITH
(7)HAVING
(10)ORDER BY
(1)
.能够快速缩小结果集的WHERE条件写在前面,如果有恒量条件,也尽量放在前面
.尽量避免使用GROUP BY 、DISTINCT 、OR 、IN 等语句的使用
.避免使用联表查询和子查询,二选一时采用连接操作,避免过多的子查询产生的CPU和IO开销
.使用执行计划来帮助分析和改进 SQL 语句
(2)
.对于大数据量的查询,尽量避免在 SQL 语句中使用 order by 字句,避免额外的开销
.如果插入的数据量很大,用 sclect into 替代 insert into 能带来更好的性能
.只关心需要的表和满足条件的数据
.使用 union all 操作代替 OR 操作
.当只要一行数据时使用 TOP 1
.拆分大的 DELETE 或 INSERT 语句,分批操作
.批量提交 SQL 语句
(3)
. 脚本专用 按不同应用场景使用不同的脚本如编辑页面与列表页面字段个数不同应使用不同脚本,而不是一个视图用遍整个系统
例:列表 SELECT <字段> From View_CM_ContractList WHERE ProjectID=1
编辑 SELECT <字段> From View_CM_ContractEdit WHERE ContractID=1
一般来说,编辑页面取得数据和列表的是不一样的,列表字段往往更少,为提升效率,应按需要编写相应的脚本
. 避免无脑嵌套,导致最终脚本臃肿不堪。
如为取一个字段的值,将一个不明底细的视图嵌入,虽然能实现功能,但隐藏的代价可能非常大。
. 脚本规范简洁
客户的业务和需求变化无常,说改就改,但我们不会一直待在原来的地方,我们有不同的项目要做,所以规范的脚本、代码,有助于后期工作的交接和扩展、优化。
整体考虑,全局出发,优先优化高并发的 SQL,而不是执行频率低某些“大”SQL