2-8-SQL的执行步骤与优化策略
SQL执行步骤
语法检查:检查 SQL 拼写是否符合语法规范
语义检查:检查访问对象是否符合存在及用户是否具有相应权限
解析:在共享池中检查是否有完全相同的之前完全解析好的,如果存在,跳过选择执行计划和产生计划,直接运行
硬解析:就是对提交的 SQL 完全重新从头进行解析,创建解析树,生成执行计划对 SQL 的执行来说是开销昂贵的动作,在很多项目中对功能相同的代码要保持一致性,用绑定变量
软解析:在共享池(shared pool)中找到了与之完全相同的 SQL 解析好的结果会跳过硬解析后面的两个步骤
执行计划:以缩排列表的方式显示 SQL 语句的执行步骤
硬件优化:
1.存取路径:B+树索引存取方法、hash索引存取方法、聚簇存取方法
2.数据的物理布局:如何存储
3.可用内存
4.可用处理器
5.集中式存储与分布式存储。集中式存储是指将所有数据都存储在同一个节点上。这样有助于提高数据库查询和修改的效率。但是集中式存储有很大的风险,若节点出现不可逆破坏,将导致数据库崩溃。比如地震导致数据库损坏。
6.高效合理的操作算法:全表扫描、索引扫描、嵌套循环连接、排序-合并连接
7.固态存储
8.RAID
索引
索引的 5 种优点
通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
在使用分组和排序子句进行数据检索时,同样可以显着减少查询中分组和排序的时间。
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
应该建立索引的条件
1) 在经常需要搜索的列上,可以加快搜索的速度;
2) 在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
3) 在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
4) 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;外键建索引由于连接加快还会减少死锁几率。
5) 在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
6) 在在经常使用在 WHERE 子句中的列上面创建索引,加快条件的判断速度。
系统(设计)
1.代数优化-优化器的启发式规则
2.计数器表
3.汇总表
4.分表
5.范式与反范式。设计合理的逻辑模式
6.外键加索引
7.尽可能避免使用自定义函数。再写 SQL语句时应尽可能避免使用自定义函数,因为对于自定义函数,优化器无法做出优化,若过度滥用自定义函数将导致数据库性能下降。但是在某些必要情况下,只能使用自定义函数来实现特定查询。
查询优化器的启发式规则
1.选择运算应尽可能先做。最重要、最基本的一条。常常可以使执行代价节约几个数量级,使计算的中间结果大大变小
2.把投影运算和选择运算同时进行。如有若干投影和选择运算,并且它们都对同一个关系操作,则可以在扫描此关系的同时完成所有这些运算以避免重复扫描关系
3.把投影同其前或后的双目运算结合起来。没有必要为了去掉某些字段而扫描一遍关系
4.把某些选择同在它前面要执行的笛卡儿积结合起来成为一个连接运算。连接运算要比同样关系上的笛卡尔积省很多时间
5.找出公共子表达式
1)范式和反范式
第一范式:每一列都是一个不可分割的原子数据项。
第二范式:第一范式基础上消除部分依赖。
第三范式:第二范式基础上消除传递依赖。
反范式是针对第三范式来说的,通过添加冗余的方式破坏了第三范式,前两个范式还是要遵循的。
范式的优点:
a.写入快,因为不需要写冗余数据,所以减少了写的负担。
b.更新快,因为通常只需要更新更少的数据。
c.由于没有冗余,所以不会造成数据不一致。
d.更少的需要GROUP BY和DISTINCT。
缺点是:需要关联。
范式的缺点,就是反范式的优点,不需要关联,并且因为在同一个表中,可以设计合适的索引。
实际应用中通常不会采用完成的范式,而是放置一些冗余,以减少表与表的关联,加快查询速度。
2)分表
如果表中的数据有状态,比如完成态和运行态,那么可以考虑将表分为运行态和完成态数据,数据转换到完成态时可以将数据归档到完成态表中,由于数据总是要运转到完成态,所以这样无论系统运行多长时间,运行态表中的数据几乎都是恒定的,而且完成态的数据除了统计分析用外,几乎不不需要查询,这样就大大提高了系统运行的速度,表中的数据量得到了控制。
另外对于统计分析的场景,为了减少表的union 可以要求业务查询从运行态和完成态两种状态中二选一。
对于一些海量数据,也可以考虑根据某个字段的值做hash,来分表存储,当然这加大了应用的复杂度,这没办法,通常没有十全十美的办法,架构就是根据实际应用场景做权衡,正所谓忠孝不能两全,只是某种办法更合适而已。
另外可以通过分布式数据库解决分表的问题,由分布式数据库自动分表存储,查询时自动合并,由分布式数据库中间件类屏蔽复杂性,各种脏活、累活交给它就是了。
3)汇总表
对于一些大数据量的报表统计工作,如果不是要求实时的话,可以定期汇总,比如每小时汇总一次或者每天汇总一次,如果要求实时的话,由于各种大表,各种group by,不但统计非常慢,而且容易影响正常的业务操作。笔者之前待的公司,每天晚上都开各种各样的定时任务进行数据汇总,在数据库不太忙的晚上,从12点干到早上6点,定时任务排的满满的,真是累死它的节奏啊,还好计算机不会闹脾气,发飙。。当然这样报表统计的数据是截止到昨天的,每次都晚一天,通常这是允许的。
4)计数器表
web应用为了记录点击次数,可以设计一个点击次数表,
create table hit_counter(cnt int unsigned not null);
由于只有一条记录这样锁争用太严重,想到了什么解决方案,同concurrenthashmap一样做锁拆分。
表结构修改为:
create table hit_counter(slot tinyint unsigned not null primary key,cnt int unsigned not null);
预先放入100条数据,这样修改的时候可以使用如下语句,
update hit_counter set cnt = cnt+1 where slot = RAND()*100;
获取的时候求和就可以了,select sum(cnt) cnt from hit_counter;
系统(应用上)
1.使用合理高效的SQL语句及方法,暗示SQL优化器进行优化
好的查询条件
关注结果集和中间数据集的大小
2.关注用户数,并发量。并据此检查自己的物理设计,系统设计
3.优化服务器配置
使用 SQL 语句要考虑的因素
1) 数据总量
Sql 考虑最重要的因素:必须访问的数据总量;没有确定目标容量之前,很难判断查询执行的效率
2) 定义结果集的查询条件
好的查询条件:满足此条件的数据很少,可以过滤很多数据
Where 字句:特别在子查询或视图中可能有多个 where 字句
过滤的效率有高有低,受到其他因素的影响
影响因素:过滤条件,主要的 sql 语句,庞大的数据对查询影响
3) 结果集的大小
查询所返回的数据量,重要而被忽略
取决于表的大小和过滤条件的细节
例外是若干个独立使用效率不高的条件结合起来效率非常高
从技术角度来看,查询结果集的大小并不重要,重要的是用户的感觉
熟练的开发者应该努力使响应时间与返回的记录数成比例
4) 获得结果集所涉及的表的数量:表的数量会影响性能
连接:太多的表连接(八张)就该质疑设计的正确性了;对于优化器,随着表数量增加,复杂度指数增长;编写太多表的复杂查询时,多种方式连接的选择失误几率很高
视图:会掩盖多表连接的事实
减少复杂查询和复杂视图
5) 并发的用户数(同时修改数据的用户数)
设计时要注意:数据块访问争用,阻塞,闩定,保证读取的一致性
一般而言,整体的吞吐量>个体响应时间
数据存贮采用固定大小的区块,可以存取多条记录,I/O 交互简单,在内存与缓冲中好处理;但是当修改后的数据太长,则会进行迁移到另一个 block 存储;数据块的太大,会带来数据块的访问争用的问题,影响并发性能
SQL 和优化器概念
优化器:借助关系理论(关系代数)提供的语义无误的原始查询进行有效的等价变换,寻找最优路径,产生新能最优的执行方案
优化:在数据处理的真正被执行的时候发生
影响优化的因素:索引,数据的物理布局,可用内存大小,可用处理器个数,直接或间接涉及的表和索引的数据量
Sql 语句先执行关系操作,再执行非关系操作(order by)
逻辑查询处理阶段简介(网上找的,理解一下)
FROM:对 FROM 子句中的前两个表执行笛卡尔积(Cartesian product)(交叉联接),生成虚拟表 VT1
ON:对 VT1 应用 ON 筛选器。只有那些使<join_condition>为真的行才被插入 VT2。
OUTER(JOIN):如 果指定了 OUTER JOIN(相对于 CROSS JOIN 或(INNER JOIN),保留表(preserved table:左外部联接把左表标记为保留表,右外部联接把右表标记为保留表,完全外部联接把两个表都标记为保留表)中未找到匹配的行将作为外部行添加到 VT2,生成VT3.如果 FROM 子句包含两个以上的表,则对上一个联接生成的结果表和下一个表重复执行步骤 1 到步骤 3,直到处理完所有的表为止。
WHERE:对 VT3 应用 WHERE 筛选器。只有使<where_condition>为 true 的行才被插入 VT4.
GROUP BY:按 GROUP BY 子句中的列列表对 VT4 中的行分组,生成 VT5.
CUBE|ROLLUP:把超组(Suppergroups)插入 VT5,生成 VT6.
HAVING:对 VT6 应用 HAVING 筛选器。只有使<having_condition>为 true 的组才会被插入
VT7.
SELECT:处理 SELECT 列表,产生 VT8.
DISTINCT:将重复的行从 VT8 中移除,产生 VT9.
ORDER BY:将 VT9 中的行按 ORDER BY 子句中的列列表排序,生成游标(VC10).
TOP:从 VC10 的开始处选择指定数量或比例的行,生成表 VT11,并返回调用者。
优化器的有效范围
优化器需要借助数据库中找到的信息
能够进行数学意义上的等价变换
优化器考虑整体响应时间
优化器改善的是独立的查询语句
策略是:如果是若干个小查询,优化器会个个优化;如果是一个大查询,优化器会将它作为一个整体优化
过滤
1) 如何限定结果集时最为关键的因素,是使用 SQL 各种技巧的判定因素
2) 过滤条件的含义:
Where 字句和 having 字句
Join 过滤条件
Select 过滤条件
3) 过滤条件的好坏,取决于
最终需要的数据是什么,来自哪些表
哪些输入值会传递到 DBMS 引擎
能过滤掉不想要的数据的条件有哪些
高效过滤条件是查询的主要驱动力
SQL 查询优化总结
1. 暗示查询优化器如何优化
使用 join 来暗示表连接顺序,当有多表连接操作时,考虑使用 exists 和 in 操作来优化;如果不使用 join 则是让查询优化器自己优化,自己确定表连接顺序(先小表,再大表),效率可能较低
2. 将多维度的查询进行降维处理,一次连接的表不要超过 3 张,超过了就将非关联子查询变成内嵌视图,降维处理
3. 考虑取出的数据在表中的比例,当查询返回记录超过数据总量 10%就不使用索引,查询结果集少于 10%是好的查询条件
4. 避免在高层使用 distinct,使用 exists 和 in 来处理
5. 避免在高层使用 select *,这样会产生冗余的结果集,降低性能
大数据量查询原则
原则:越快踢出不要的数据,查询的后续阶段必须处理的数据量就缺少,查询效率越高
应用:
集合操作,如 union 语句,但是不要 cut-and-paste
Group by&having 字句
所有影响聚合函数的结果条件都应该放在hanving字句中
任何无关聚合条件都应该放在 where 子句中
减少 group by 必须执行排序操作所处理的数据量
非关联子查询变成内嵌视图— 降低查询维度
例子:
‘’
映像语句的处理顺序
1. 合并FROM 子句中的表 ( 笛卡儿乘积)
2. 利用WHERE子句中的条件进行元组选择,抛弃不满足子句中的条件进行元组选择,抛弃不满足WHERE条件的那些元组
3. 根据GROUP BY子句对保留下来的元组进行分组
4. 利用HAVING子句中的条件对分组后的元组集合 子句中的条件对分组后的元组集合(group) 进行选择,抛弃不满足HAVING条件的那些元组
5. 根据SELECT子句进行统计计算,生成结果关系中的元组子句进行统计计算,生成结果关系中的元组
6. 根据ORDER BY子句对查询结果进行排序