从 SQL 查询优化技巧去看 h2 数据库查询原理
本文目标是:了解查询的核心原理,对比 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);
}
// 保存符合条件的数据,这个对应 resultSet
result.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() {
// 遍历结束,没有符合的条件的 row
if (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,直到找到符合的 row
while (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 ok
if (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.
作者:京东物流 杨攀
来源:京东云开发者社区 自猿其说Tech 转载请注明来源
从 SQL 查询优化技巧去看 h2 数据库查询原理的更多相关文章
- MySQL SQL查询优化技巧详解
MySQL SQL查询优化技巧详解 本文总结了30个mysql千万级大数据SQL查询优化技巧,特别适合大数据里的MYSQL使用. 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 ...
- 30个mysql千万级大数据SQL查询优化技巧详解
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...
- springboot09 事务 H2数据库
一.事务 1. 事务介绍 事务可以包含多个操作步骤 , 如果有一个步骤失败,那么这一组都以失败告终. 事务是指包含多个微小逻辑单元的一组操作, 只要其中有一个逻辑失败了,那么这一组操作就全部以失败告终 ...
- MySQL 性能调优——SQL 查询优化
如何设计最优的数据库表结构,如何建立最好的索引,以及如何扩展数据库的查询,这些对于高性能来说都是必不可少的.但是只有这些还不够,要获得良好的数据库性能,我们还要设计合理的数据库查询,如果查询设计的很糟 ...
- 数据库查询服务DBCacheServer
各个业务系统,都需要查询各类数据库; 一般查询数据库都是客户端自己连接,根据现在的情况,存在以下几点问题 1.客户端连接很多,连接大小,峰值不可控 2.客户端SQL程序员自己写,参差不齐 3.SQL书 ...
- [转载]Hibernate如何提升数据库查询的性能
目录(?)[-] 数据库查询性能的提升也是涉及到开发中的各个阶段在开发中选用正确的查询方法无疑是最基础也最简单的 SQL语句的优化 使用正确的查询方法 使用正确的抓取策略 Hibernate的性能优化 ...
- Django 数据库查询
#!/usr/bin/python #coding:utf-8 from django.shortcuts import render; from django.shortcuts import re ...
- MS SQL Server数据库查询优化技巧
[摘 要]本文主要是对MS SQL Server数据库查询优化技巧进行了说明和分析,对索引使用.查询条件以及数据表的设计等进行了阐述.中国论文网 http://www.xzbu.com/2/view- ...
- 数据库查询优化-20条必备sql优化技巧
0.序言 本文我们来谈谈项目中常用的 20 条 MySQL 优化方法,效率至少提高 3倍! 具体如下: 1.使⽤ EXPLAIN 分析 SQL 语句是否合理 使⽤ EXPLAIN 判断 SQL 语句是 ...
- 从SQL Server CloudDBA 看云数据库智能化
最近阿里云数据库SQL Server在控制台推出了CloudDBA服务,重点解决数据库性能优化领域问题,帮助客户更好的使用好RDS数据库,这是继MySQL之后第二个关系型数据库提供类似的服务. 数 ...
随机推荐
- Centos7安装Python3.x
一.修改yum源 查看Centos发行版本 cat /etc/redhat-release 换阿里云yum源 备份原始yum源 mv /etc/yum.repos.d/CentOS-Base.repo ...
- Blazor中CSS隔离无法用在子组件内部
Blazor的CSS隔离是个很好的东西,如图,只需添加一个与Razor组件同名的CSS文件,这个文件中的CSS样式只会在同名的Razor组件中使用. 原理是通过在dom元素添加一个代表标识符的属性 在 ...
- 树形DP + 换根DP
树形DP--基础 P1352 没有上司的舞会 设 \(f[i][0/1]\) 表示第 \(i\) 个人不去或者去. 如果第 \(i\) 个人没去,那么下属可去可不去,所以 \(f[i][0] = \s ...
- oracle 11g手工建库步骤(初学者)
要建立的数据库ORACLE_SID=test1sys和system的密码为oracle1.建立相应的目录mkdir /u01/app/oracle/oradata/test1mkdir /u01/ap ...
- asp.net core之HttpClient
本文介绍了ASP.NET Core中的HttpClient和HttpClientFactory的作用.用法以及最佳实践.通过示例代码的展示,读者可以了解如何使用HttpClient发送HTTP请求并处 ...
- Jenkins用户管理(二):不同用户分配不同的任务访问权限
需求:不同用户访问到不同的Jenkins任务. 依赖插件:Role-based Authorization Strategy 1. 插件安装 进入[系统管理]-[插件管理]-[可用插件],搜索Role ...
- 痞子衡嵌入式:AppCodeHub - 一站网罗恩智浦MCU应用程序
近日,恩智浦官方隆重上线了应用程序代码中心(Application Code Hub,简称 ACH),这是恩智浦 MCUXpresso 软件生态的一个重要组成部分.痞子衡之所以要如此激动地告诉大家这个 ...
- 关于package-lock.json
前言 上篇文章我们了解了package.json,一般与它同时出现的还有一个package-lock.json,这两者又有什么关系呢?下面一起来了解吧. 介绍 package-lock.json 它会 ...
- C++欧几里得算法求最大公约数和最小公倍数
定义 最大公约数即为 Greatest Common Divisor,常缩写为 gcd. 一组整数的公约数,是指同时是这组数中每一个数的约数的数. 一组整数的最大公约数,是指所有公约数里面最大的一个. ...
- 三维模型OSGB格式轻量化的跨平台兼容性技术分析
三维模型OSGB格式轻量化的跨平台兼容性技术分析 在三维模型应用中,OSGB格式轻量化处理是一种常见的技术手段,可以通过数据压缩.简化.滤波等操作,降低三维模型数据的存储空间和传输带宽需求,提高应用程 ...