Insight H2 database 数据查询核心原理

本文目标是:了解查询的核心原理,对比 SQL 查询优化技巧在 h2database 中的落地实现。

前提:为了贴近实际实际,本文 Code Insight 基于 BTree 存储引擎。

数据查询核心原理

数据库实现查询的原理:遍历表/索引,判断是否满足where筛选条件,添加到结果集。简单通用。

对于选择表还是索引、如何遍历关联表、优先遍历哪个表、怎样提升遍历的效率,这个就是数据库查询复杂的地方。

/*** 查询命令实现查询的主要过程* @see org.h2.command.dml.Select#queryFlat*/
private void queryFlat(int columnCount, ResultTarget result, long limitRows) {// 遍历单表 or 关联表。topTableFilter 可以简单理解为游标 cursor。while (topTableFilter.next()) {// 判断是否符合 where 筛选条件if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {Value[] row = new Value[columnCount];// 填充select 需要的 columns ①for (int i = 0; i < columnCount; i++) {Expression expr = expressions.get(i);row[i] = expr.getValue(session);}// 保存符合条件的数据,这个对应 resultSetresult.addRow(row);// 没有 sort 语句的情况下,达到 limitRows, 终止 table scan ②if ((sort == null || sortUsingIndex) && limitRows > 0 &&result.getRowCount() >= limitRows) {break;}}}
}

Join 查询核心原理

基于状态机模式,实现多表嵌套循环遍历。

使用的 Join 算法是: Nested Loop Join。

状态变迁:BEFORE_FIRST --> FOUND --> AFTER_LAST

/*** Check if there are more rows to read.* 遍历的数据 row 记录在当前 session 中,随时随地可以获取** @return true if there are* @see org.h2.table.TableFilter#next*/
public boolean next() {// 遍历结束,没有符合的条件的 rowif (state == AFTER_LAST) {return false;} else if (state == BEFORE_FIRST) {// cursor 遍历初始化, 如果基于索引的游标,则可以提前锁定数据范围。③cursor.find(session, indexConditions);if (!cursor.isAlwaysFalse()) {// 如果包含 join 表,重置关联表的状态机。if (join != null) {join.reset();}}} else {// state == FOUND || NULL_ROW 的情况// 嵌套遍历 join 关联表。这是个递归调用关联表的过程。if (join != null && join.next()) {return true;}}// 表/索引数据扫描,匹配filterCondition,直到找到符合的 rowwhile (true) {if (cursor.isAlwaysFalse()) {state = AFTER_LAST;} else {if (cursor.next()) {currentSearchRow = cursor.getSearchRow();current = null;state = FOUND;} else {state = AFTER_LAST;}}// where 条件判断if (!isOk(filterCondition)) {continue;}// 嵌套遍历 join 关联表。主表的每一行 row,需要遍历关联子表一次。④if (join != null) {join.reset();if (!join.next()) {continue;}}// check if it's okif (state == NULL_ROW || joinConditionOk) {return true;}}state = AFTER_LAST;return false;
}

获取查询数据

从遍历的 row 中,获取 select 语句需要的 column 数据。

对应的 Cursor 实现是:org.h2.index.PageBtreeCursor

/*** 根据 columnId 获取对应的值* @see org.h2.table.TableFilter#getValue*/
public Value getValue(Column column) {if (current == null) {// 优先从当前遍历的 row 获取数据。// 如果是索引中的 row,不会包含所有的行,会有取不到的情况Value v = currentSearchRow.getValue(columnId);if (v != null) {return v;}// 如果没有,再尝试从原始表 row 存储中获取数据。⑤// 对应的实现: currentRow = index.getRow(session, currentSearchRow.getKey());current = cursor.get();if (current == null) {return ValueNull.INSTANCE;}}return current.getValue(columnId);
}

常用的 SQL 查询优化技巧

分别对应上述源代码注释的数字角标。

①避免使用 SELECT *:只选择需要的列

如果使用 select *, 即使使用了索引查询。也需要取原数据行的所有数据(⑤)。会进行数据的二次读取,也就是回表查询。影响了性能。

②避免使用 ORDER BY, 尽量使用LIMIT

使用 LIMIT:如果只需要部分结果,可以使用 LIMIT 子句限制返回的行数,避免检索整个结果集。

如上源代码,如果没有 Order By,有limit 限制情况下,可以中途结束表遍历。

如果有 Order By 的情况下,肯定要执行完成整个扫描遍历的过程,最终在 result 结果集中再一次进行排序计算。

③使用索引:确保表中的列上有适当的索引,以加快查询速度。

如果使用索引,在初始化扫描阶段,会给 cursor 一定的范围,避免全表扫描。极大的缩小的查询范围。

④减少连接的表的数量:如果可能,尽量减少查询中的表的数量。

无需多言,嵌套递归查询,理论上是所有表的笛卡尔积。

使用覆盖索引:一个查询的所有列都包含在索引中。

这样查询可以只扫描索引而不需要回表。例如,如果你的查询是 SELECT id, name FROM users WHERE age = 30,那么在 age, id, name 上创建一个复合索引可以避免回表。

其他

Nested Loop Join

// 用伪代码表示,可以更清晰理解上述 join 遍历的过程
for (r in R) {for (s in S) {if (r satisfy condition s) {output <r, s>;}}
}

MySQL 中的Nested Loop Join

MySQL官方文档中提到,MySQL只支持Nested Loop Join这一种join algorithm.

MySQL resolves all joins using a nested-loop join method.

This means that MySQL reads a row from the first table, and then finds a matching row in the second table, the third table, and so on.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/101619.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试工具有什么作用?有哪些好用的测试工具推荐?

软件测试工具是现代软件测试中不可或缺的重要组成部分&#xff0c;指的是一系列在软件开发过程中使用的工具&#xff0c;用于帮助测试人员进行测试活动&#xff0c;提高测试效率&#xff0c;减少测试成本。选择并使用合适的软件测试工具&#xff0c;可提高软件质量和效率。 一…

WebRTC 系列(四、多人通话,H5、Android、iOS)

WebRTC 系列&#xff08;三、点对点通话&#xff0c;H5、Android、iOS&#xff09; 上一篇博客中&#xff0c;我们已经实现了点对点通话&#xff0c;即一对一通话&#xff0c;这一次就接着实现多人通话。多人通话的实现方式呢也有好几种方案&#xff0c;这里我简单介绍两种方案…

【Ubuntu】Ubuntu18.04终端卡顿问题

博主您好&#xff0c;我也遇到了类似的问题&#xff0c;但我找到了问题的原因&#xff1a; 在gnome-terminal中&#xff0c;按tab补全是默认开启了“咚咚咚”音效的&#xff0c;在gnome-terminal里把音效关掉就好了&#xff0c;主要是因为按tab时&#xff0c;NVIDIA的视频信号和…

应用在SMPS中的GaN/氮化镓

开关模式电源&#xff08;Switch Mode Power Supply&#xff0c;简称SMPS&#xff09;&#xff0c;又称交换式电源、开关变换器&#xff0c;是一种高频化电能转换装置&#xff0c;是电源供应器的一种。其功能是将一个位准的电压&#xff0c;透过不同形式的架构转换为用户端所需…

uniapp 跳转到指定位置

this.$router.push({name: Demo,params: {id: 123} })这样就实现了页面的跳转&#xff0c;并且将参数id传递给了Demo组件。 如果需要跳转到当前页面的不同位置&#xff0c;我们可以使用锚点来实现。锚点是一个HTML元素的标识符&#xff0c;可以用于定位和跳转到该元素。例如&a…

【2023】M1/M2 Mac 导入Flac音频到Pr的终极解决方案

介绍 原作者链接&#xff1a;https://github.com/fnordware/AdobeOgg 很早之前就发现了这个插件&#xff0c;超级好用&#xff0c;在windows上完全没有问题&#xff0c;可惜移植到mac就不行了&#xff08;然后我给作者发了一个Issue&#xff0c;后来就有大佬把m1的编译出来了&…

②. GPT错误:图片尺寸写入excel权限错误

꧂问题最初 ꧁ input输入图片路径 print图片尺寸 大小 长宽高 有颜色占比>0.001的按照大小排序将打印信息存储excel表格文件名 表格路径 图片大小 尺寸 颜色类型 占比信息input输入的是文件就处理文件 是文件夹&#x1f4c1;就处理文件。路径下的图片 1. 是处理本路径图片 …

狄拉克函数及其性质

狄拉克函数及其性质 狄拉克函数 近似处理 逼近近似 积分近似 狄拉克函数的性质 狄拉克函数的Hermite展开

构建图像金字塔:探索 OpenCV 的尺度变换技术

构建图像金字塔&#xff1a;探索 OpenCV 的尺度变换技术 引言什么是图像金字塔&#xff1f;为什么需要图像金字塔&#xff1f;构建高斯金字塔构建拉普拉斯金字塔图像金字塔的应用示例&#xff1a;在不同尺度下检测图像中的边缘 结论 引言 在计算机视觉领域&#xff0c;图像金字…

ROS-PX4仿真笔记_1

offbord模式测试 rosrun offboard_pkg position stablelize模式 lqr控制器实验 roslaunch px4 fast_test.launch 无人机起飞1.5-2m sh mybot_gazebo.sh先点击mode&#xff0c;再点击cmd&#xff0c;才能打开offbord模式 minijerk实验 roslaunch px4 fast_test.launch sh …

电子科大软件系统架构设计——系统架构设计

文章目录 系统架构设计系统设计概述系统设计定义系统设计过程系统设计活动系统设计基本方法系统设计原则系统设计方法分类面向对象系统分析与设计建模过程 系统架构基础系统架构定义系统架构设计定义系统架构作用系统架构类型系统总体架构系统拓扑架构系统拓扑架构类型系统拓扑…

读书笔记:多Transformer的双向编码器表示法(Bert)-4

多Transformer的双向编码器表示法 Bidirectional Encoder Representations from Transformers&#xff0c;即Bert&#xff1b; 第二部分 探索BERT变体 从本章开始的诸多内容&#xff0c;以理解为目标&#xff0c;着重关注对音频相关的支持&#xff08;如果有的话&#xff09;…

C语言--sizeof()

sizeof() 是C语言中的一个操作符&#xff0c;用于获取数据类型或对象的大小&#xff0c;通常以字节为单位。它返回一个整数值&#xff0c;表示所查询对象或数据类型占用的内存字节数。sizeof() 可以用于不同的用途&#xff1a; 获取数据类型的大小&#xff1a;您可以使用 size…

Docker基础操作容器

启动容器有两种方式&#xff0c;一种是基于镜像新建一个容器并启动&#xff0c;另外一个是将在终止状态&#xff08;exited&#xff09;的容器重新启动。 因为 Docker 的容器实在太轻量级了&#xff0c;很多时候用户都是随时删除和新创建容器。 新建并启动 所需要的命令主要…

使用tailwindcss来构建以及引入外部组件

使用tailwindcss来构建以及引入外部组件 使用tailwindcss来构建以及引入外部组件 前言构建组件 核心思想可行方案不可行方案 可行方案详解 custom css selector Functions & Directivesadd prefixadd scoped不打包 构建demo链接相关issues 前言 我们在日常的开发中&am…

日志的详细说明

在Spring应用程序中&#xff0c;日志是指记录应用程序运行时信息的一种机制&#xff0c;通常以文本形式保存在文件中或通过其他适当的渠道显示。日志是开发和维护应用程序时非常重要的工具&#xff0c;用于记录应用程序的状态、错误、调试信息和性能数据&#xff0c;以便开发人…

1、AM64xx的SDK重新编译lib文件

当需要修改AM64XX的SDK提供的源文件时&#xff0c;如果要在自己的工程使用&#xff0c;需要重新编译出lib&#xff0c;下面是编译lib的具体方法&#xff1a; 因为没有ccs编译出lib的工程&#xff0c;所以需要再命令行模式下生成lib文件 1、配置好gmake环境 如果安装了ccs&am…

隔离上网,安全上网

SDC沙盒数据防泄密系统&#xff08;安全上网&#xff0c;隔离上网&#xff09; •深信达SDC沙盒数据防泄密系统&#xff0c;是专门针对敏感数据进行防泄密保护的系统&#xff0c;根据隔离上网和安全上网的原则实现数据的代码级保护&#xff0c;不会影响工作效率&#xff0c;不…

SP605官方开发板不能扫到链的问题

很早之前的板子&#xff0c;近些天需要重新搞FPGA&#xff0c;所以又拿出来&#xff0c;应该以前都是在win7下开发&#xff0c;现在都win10了&#xff0c;vivado都不支持sp6&#xff0c;所以先得下载一个14.7版本&#xff0c;但是出现了新的问题&#xff0c;就是不能扫到链。 …

本文整理了Debian 11在国内的几个软件源。

1&#xff0e;使用说明 一般情况下&#xff0c;将/etc/apt/sources.list文件中Debian默认的软件仓库地址和安全更新仓库地址修改为国内的镜像地址即可&#xff0c;比如将deb.debian.org和security.debian.org改为mirrors.xxx.com&#xff0c;并使用https访问&#xff0c;可使用…