应用最广的两类数据库的区别、优势对比、查询优化方法及案例实践
ccwgpt 2025-01-06 11:42 78 浏览 0 评论
1、通用数据库分类
1.1关系型数据库
关系型数据库是多个二维数据表的集合,数据以二维数据表的形式进行存储,数据表之间可以通过应用程序或者数据的主、外键建立特定的关联关系,让数据之间存在特定的应用联系。他的优点主要是数据访问简便,通过结构化查询语言SQL进行单一数据表或多数据表的联合查询、修改和删除操作。相较于非关系型数据库,对于大规模的数据的读写操作效率不高。
1.2非关系型数据库
非关系型数据库是各种存储方式的集合,存储形式包括图像、文本等,它不以关系型数据表的形式储存。他可以使用专用的硬件设备提升大规模数据的读写效率,通常被用作分析性场景。
2、常用关系型数据库查询过程原理
对于关系型数据库来说,不管是MYSQL、ORACLE还是DB2其数据库的查询过程通常都分为5个步骤,以DB2为例其包括语法和语义检查、选择最优访问路径、生成数据库可识别的执行代码、按SQL命令进行数据操作等。下面以DB2为例对上述过程进行详细的介绍。
1)Precompile
Precompile是DB2的预编译,由于大部分的编译器不能识别SQL 语句,需要在执行编译(COMPILE)之前使用DB2的预编译器防止编译器在编译过程中出错。其中数据库会将程序分离成2部分。一部分是数据库从应用程序中剥离出的应用逻辑部分,我们称之为LOAD MODULE,该文件用作后续的COMPILE;另一部分是SQL语句,我们称之为DBRM,这部分就是我们执行数据库操作的代码。LOAD MODULE与DBMR通过token进行关联,保证两部分内容在应用执行时可以完美的匹配。
2)cics translation
该部分数据库会对cics相关的执行进行解析和转义,以保证在编译阶段数据库对cics相关指令可以识别
3)Compile
该部分数据库编译器会分析源程序并将源码翻译成机器指令,SQL的语义与语法检查都是通过该部分进行。
4)访问路径选择
该部分是数据库对sql语句的访问路径进行择优选择,该部分通过数据库的optimizer完成,实际工作会在Compile阶段一并完成。
3、优化器最优访问路径选取原则
以DB2为例,数据库通过optimizer进行访问路径的选择,通常我们所说的sql语句优化大部分都是对查询语句的优化。数据库在执行一条sql语句的时候可以通过多条道路实现一条数据的读取功能,比如对于同一条记录的查询,数据库可以通过索引进行数据的筛查,我们称之为index scan 或者index only。对于多索引的数据表来说,针对不同的索引,访问路径又有多条道路可以选择。又或者数据库也可不使用索引而是通过全表扫描的方式进行数据读取。那么最终数据库会选择何种路径进行数据读取操作呢?这个就是最优访问路径的选择问题。
数据库优化器最优访问路径选取的方法并不复杂,其内部有一套标准的算法,首先数据库会将所有可获取到需求数据的访问路径进行读取成本的估算。并对所有路径估算出的开销进行比较,选择开销最小的作为这一条数据访问语句的最优访问路径以达到提升访问效率的目的。
影响访问路径选择的因素有很多,包括数据表的索引建立方式,如唯一索引、主键、分区索引、非分区索引、cluster index和非cluster index等等。同时数据表数据的摆布方式,包括数据表键值的重复率等等也对访问路径的选择起到很大的作用,甚至执行sql语句数据库所在的硬件设备容量也在访问路径选择的考量之中。
数据库最优访问路径的选取实际上就是访问开销最小的访问路径的选取,目的是降低sql语句执行时候的cpu和磁盘I/O的消耗,提升访问效率。
4、高效查询语句书写原则
4.1避免不恰当的使用“SELECT *”
如果 SQL 语句使用了“SELECT *”,DB2 就需要把表的所有列都进行处理并且返回给用户,这显然会增加 I/O 和 CPU 的开销。如果这条 SQL 语句还包括了排序(Sort)操作(比如 ORDER BY),那么对全部这些列进行排序也会影响到性能。而且当表定义的列越多,每个列定义的数据类型(Data type)长度越长,这对性能的影响就可能越明显。
4.2避免在本地谓词的列上使用复杂表达式
本地谓词(Local predicate)是与连接谓词(Join predicate)相对应,它一般是指该谓词当中只包含一个表上的一个列。YEAR(Date) > 2004 和 Amount > 1000 都是两个本地谓词。然而在前一个谓词 YEAR(Date) > 2004 中,它对 Date 这个列有一个函数 YEAR 的调用。在这种情况下,即使 Date 上存在一个索引,DB2 也无法使用这个索引来访问数据。如果能够在确保语义不变的前提下,适当改写这个谓词,避免在 Date 列上调用函数,那么情况可能会有所不同。例如,这个谓词可以改写为如下的样子:
Date > ‘ 2004-12-31 ’
下面来看一个具体的示例:
SQL 1:select l_quantity, l_comment from lineitem
where l_orderkey + 100 = 200
SQL 2:select l_quantity, l_comment from lineitem
where l_orderkey = 100
SQL 1 的访问路径图
SQL 2 的访问路径图
4.3避免用复杂表达式构建连接谓词
让数据库可以有多种访问路径的选择,避免由于人为编写不恰当的SQL语句限制优化器的选择。
T1.C1=T2.C2 就是一个典型的连接谓词,这种写法也是常见的连接谓词的形式。对于这种常见的连接谓词,DB2 可以考虑采用几种不同的表连接方式(Join Method),常见的连接方法有嵌套循环连接(Nested-Loop-Join, NLJ),归并排序连接(Merge-Scan-Join, MSJoin),哈希连接(Hash-Join)等。DB2 优化器会根据实际情况选择从中选取一个性能最佳的来将 T1 和 T2 连接起来。
但是在连接谓词 T1.C1 * T1.C2 = T2.C2 中,“=”左边不是一个列名,而是一个表达式,它涉及 T1 表上不同列之间的计算。对于这样一个用复杂表达式构建的连接谓词,DB2 只能用 Nested-Loop-Join 这种最基本的方式来建立 T1 和 T2 之间的连接,而不考虑用其他的连接方式,从而也就无法选择最优的连接方式。
看下面这个示例:
SQL 1:
SELECT l_comment, o_comment FROM lineitem, order
WHERE l_orderkey = o_orderkey + 100
SQL 2:
SELECT l_comment, o_comment FROM lineitem, order
WHERE l_orderkey = o_orderkey
SQL 1 的访问路径图
SQL 2 的访问路径图
如果想在满足业务逻辑需求的情况下,同时保证连接谓词的简洁,也可以考虑增加一个新的列(例如 SQL 1 中,定义新的列 o_orderkey2,其值等于 o_orderkey + 100),直接构造连接谓词(l_orderkey = o_orderkey2),从而最大程度的提高 SQL 语句的性能。
4.4避免连接谓词中的数据类型不一致
在用连接谓词连接不同的表的时候,即使对于 T1.C1=T2.C1 这样典型的连接谓词,也应该确保 T1.C1 和 T2.C1 具有同样的数据类型。连接谓词中两个列的数据类型定义的不一致会导致 DB2 放弃使用某些表连接方式。比如 Hash-Join 这种表连接方式对连接谓词就有更多的限制条件,条件之一就是连接谓词中的两个列的数据类型必须完全一致,否则 Hash-Join 不能使用。此外,如果 T1.C1 的数据类型是 CHAR,GRAPHIC,DECIMAL 或者 DECFLOAT,那么 T2.C1 除了需要是相同的数据类型外,它所定义的数据类型的长度也需要和 T1.C1 一致。更多的表连接方式意味着 DB2 可以有更多的选择来将表连接在一起,并从中选出最优的方案。
看下面这个示例:
SQL 1: SELECT l_comment, o_comment FROM lineitem, order
WHERE l_orderkey = o_orderkey
对于 SQL 1,DB2 优化器采用了归并排序 (MSJoin) 的连接方法对两个数据表进行了连接操作 ,这里连接谓词 l_orderkey = o_orderkey 中的 2 个列的数据类型完全一致都为 integer 类型。如果改动其中一个列的数据类型为 double 类型,此时 DB2 就只能采用嵌套循环连接方法进行连接操作,而对比之后就会发现,使用嵌套循环连接的 Total Cost 较高,这意味着性能较差。
INTEGER = INTEGERINTEGER= DOUBLE
4.5确保在连接谓词中使用等号
典型的连接谓词通常是形如 T1.C1=T2.C1 这样的形式,注意到这里是用“=”这个操作符将左右两边的列连接起来。理论上,也可以使用其他的操作符来构造连接谓词,比如“<”或者“>”这样的比较运算符。但是实际上基于性能的考虑,在连接谓词中应该只使用“=”,尽量避免使用其他的比较运算符。
在连接谓词 T1.C1 < T2.C1 中,使用了“<”这个比较运算符。 对于这样的 SQL 语句,DB2 只能采用 Nested-Loop-Join 这种最基本的方式来建立 T1 和 T2 之间的连接,而不考虑用其他的连接方式。在某些实际的应用场景当中,出于业务逻辑上的要求,出现 T1.C1 < T2.C1 这样的连接谓词可能是不可避免的。在这种情况下,基于性能优化的考虑,应该在 T1 和 T2 上都建立适当的索引,使得 T1.C1 < T2.C1 这个谓词能够使用索引。其中的原因在于,DB2 只能使用 Nested-Loop-Join 来建立 T1 和 T2 之间的连接,此时应该确保有合适使用的索引能够让 Nested-Loop-Join 采用 Index-Scan 这种数据访问方法,从而尽可能提高性能。
看下面的示例,
SQL 1: SELECT l_comment, o_comment FROM lineitem, order
WHERE l_oderkey >o_orderkey
SQL 2: SELECT l_comment, o_comment FROM lineitem, order
WHERE l_oderkey =o_orderkey
在 SQL 1 中连接谓词是通过大于号连接的,DB2 只能采用嵌套循环连接 (Nested-Loop-Join) 这种最基本的方式来建立两个表之间的连接。在 SQL2 中连接谓词中采用“=”连接,此时 DB2 优化器选用了归并排序(MSJoin)的连接方式,它的 Total cost 比 SQL 1 的要低很多,具有较好的性能。注意这里 SQL 1与 SQL 2 在语义上是不等价的,在这里用这样的示例是为了说明连接谓词中不使用等号的写法会导致访问路径完全不同,从而影响查询性能。
SQL 1 的访问路径图
SQL 2 的访问路径图
4.6恰当安排 Group By 子句中列的顺序
通常情况下,SQL 语句中的 GROUP BY 子句会导致数据库不得不通过一个排序(SORT)操作来实现对数据的分组,而排序被认为是一个比较耗费 CPU 和内存的操作。实际上某些情况下,如果写法得当,当中的排序操作是可以避免的。具体来说,在写 GROUP BY 子句的时候,应该考虑到数据库中已经存在的索引的情况。如果 GROUP BY 子句中所有的列恰好包括在某个索引的键(Key column)的范围之内而且是处于开始的位置,那么在写 GROUP BY 子句的时候,就应该按照该索引上键的先后顺序来写 GROUP BY 子句。
比如说有如下的 SQL 语句:
SELECT C2, C3, C1, AVG(C4)
FROM T1
GROUP BY C2, C3, C1
一般情况下,GROUP BY C2, C3, C1这样的写法都会导致数据库的一个排序操作。但假定表 T1 上已经存在一个索引 IX1(C1, C2, C3, C4), 这里注意到 GROUP BY 子句中引用到的列(C2,C3,C1)正好是索引 IX1 中的前三个键,那么就可以通过改变 GROUP BY 子句中列的顺序的办法来避免这个排序操作。
可以把 SQL 语句改写为如下所示:
SELECT C1, C2, C3, AVG(C4)
FROM T1
GROUP BY C1, C2, C3
通过这样改变 GROUP BY 子句中列的顺序使其与索引 IX1 中的键顺序一致,数据库就可以利用 IX1 来访问其已经排序的键值并直接返回进行下一步操作,从而避免额外的排序操作,从而带来查询性能上的提高。
需要指出的是,通过这样改写 GROUP BY 子句来避免排序,可能会导致最终返回结果的顺序不一致。在实际的业务逻辑当中,需要用户来确认是否其关注返回结果的顺序性。
下面来看一个具体的示例:
SQL 1:SELECT AVG(o_shippriority) FROM order
GROUP BY o_custkey , o_orderkey, o_orderdate
SQL 2:SELECT AVG(o_shippriority) FROM order
GROUP BY o_orderkey, o_orderdate, o_custkey
这里 2 个 SQL 唯一的差别就在于 GROUP BY 子句中列的顺序不同。假定order 表上存在一个索引 PXO@OKODCKSPOP (O_ORDERKEY,O_ORDERDATE,O_CUSTKEY,O_SHIPPRIORITY,O_ORDERPRIORITY)。
由于 SQL 1 中的 GROUP BY 子句的列顺序与索引 PXO@OKODCKSPOP 的键顺序不一致,DB2 无法直接利用这个索引,所以 DB2 需要基于这 3 个列做一次排序(Sort),然后进行分组合并,排序的结果还需要通过临时文件(Wkfile)来保存。如果调整 GROUP BY 子句中的列顺序如 SQL 2 所示,使其与索引 PXO@OKODCKSPOP 的键顺序一致,DB2 通过这个索引返回的结果就已经是有序的,这样就省去了排序操作。对比两者的访问路径图可以看出来,SQL 2 所花费的成本(Total Cost)会少很多,性能上有较大的提高。
SQL 1 的访问路径图
SQL 2 的访问路径图
4.7活用FETCH FIRST N ROWS ONLY字句
在 DB2 的 SQL 语法中,FETCH FIRST n ROWS ONLY 表示只取回结果集当中的前 n 条记录,在应用中经常会碰到判断满足条件的数据是否存在这样的逻辑,为达到目的有些程序员会用count(*)的结果来作为判断依据,众所周知count(*)的性能是不高的,对于此类场景可以考虑用FETCH FIRST 1 ROWS ONLY替代。
4.8多用indexable 谓词避免使用Non-indexable谓词
indexable的布尔类型谓词才能成为用来匹配索引的谓词,而Non-indexable的谓词是不能用来匹配索引的,在谓词设计中,首先需要应用的基本原则是尽量避免使用Non-indexable 谓词。
Non-indexable谓词包括:
1)谓词左边的列在表达式或函数中 DATE + 10 < CURRENTDATE, LENGTH(C1) = 4
2)与字段比较的对象是不同类型的值或变量,如变量定义与字段类型不符。
3)字段之间的比较 C1 < C2
4)BETWEEN col1 AND col2
5)非谓词,即包含NOT,NOT NULL,NOT LIKE,NOT IN,NOT BETWEEN,<>
6)LIKE谓词使用时将通配符放在最前面。C1 LIKE ‘%ABC’
4.9谓次匹配尽量多的索引字段
一个查询语句谓词与索引的key column匹配越多,过滤率越高,查询的效率也越好。
4.10谓词中加入分区表分区键值
对于分区表而言,尽可能在SQL语句中加入分区键值列作为谓词,将查询范围限定在某一个或某几个分区中,避免整表扫描,可以极大地减少I/O开销。
5、总结
本文主要介绍了当下使用最广泛的两类数据库(关系型数据库及非关系型数据库)的区别和各自优势进行了简要阐述。从农行核心应用实际出发,重点从开发人员角度,对关系型数据库查询语句的书写与数据库访问性能的关系进行了阐述,通过实例给出了高效SQL语句的书写方法及背后的技术原理。
请关注+私信回复:“测试”就可以拿到测试开发8大进阶技术图谱及软件测试免费学习资料包,快速让自己变强!
相关推荐
- 一个基于.Net Core遵循Clean Architecture原则开源架构
-
今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...
- AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%
-
写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...
- OneCode低代码平台的事件驱动设计:架构解析与实践
-
引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...
- 国内大厂AI插件评测:根据UI图生成Vue前端代码
-
在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...
- AI+低代码技术揭秘(二):核心架构
-
本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...
- GitDiagram用AI把代码库变成可视化架构图
-
这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...
- 30天自制操作系统:第六天:代码架构整理与中断处理
-
1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...
- AI写代码越帮越忙?2025年研究揭露惊人真相
-
近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...
- 一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具
-
一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...
- 5分钟掌握 c# 网络通讯架构及代码示例
-
以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...
- 从复杂到优雅:用建造者和责任链重塑代码架构
-
引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...
- 低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈
-
专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...
- 框架设计并不是简单粗暴地写代码,而是要先弄清逻辑
-
3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...
- 大佬用 Avalonia 框架开发的 C# 代码 IDE
-
AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...
- 轻量级框架Lagent 仅需20行代码即可构建自己的智能代理
-
站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 框架图 (58)
- flask框架 (53)
- quartz框架 (51)
- abp框架 (47)
- springmvc框架 (49)
- 分布式事务框架 (65)
- scrapy框架 (56)
- shiro框架 (61)
- 定时任务框架 (56)
- java日志框架 (61)
- mfc框架 (52)
- abb框架断路器 (48)
- beego框架 (52)
- java框架spring (58)
- grpc框架 (65)
- tornado框架 (48)
- 前端框架bootstrap (54)
- orm框架有哪些 (51)
- 知识框架图 (52)
- ppt框架 (55)
- 框架图模板 (59)
- 内联框架 (52)
- cad怎么画框架 (58)
- ssm框架实现登录注册 (49)
- oracle字符串长度 (48)