从 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之后第二个关系型数据库提供类似的服务. 数 ...
随机推荐
- CSDN这么公然爬取(piao qie)cnblogs的文章,给钱了吗?
在CSDN网站经常看到有博客转载cnblogs的文章,开始还以为是网友自行转载,后来才发现,这些所谓的转载应该都是机器爬取(piao qie)过去的.不知道cnblogs对此怎么看. 下面看看几个示例 ...
- vulnhub billu:b0x
知识点 SQLi.目录爆破.数据库操作.文件包含漏洞.提权.反弹shell 解题步骤 nmap扫描有80,22端口 nmap -sV -Pn -T 4 192.168.220.132 访问网页提示sq ...
- VUE3、ElementPlus 重构若依vue2 表单构建功能
Vue3 + ElementPlus + Vite 重构 若依Vue2 表单构建功能 若依官方的Vue3 版本发布已经有段时间了,就是这个表单构建功能一直没有安排计划去适配到Vue3! 前段时间公司需 ...
- 面试再也不怕问ThreadLocal了
要解决多线程并发问题,常见的手段无非就几种.加锁,如使用synchronized,ReentrantLock,加锁可以限制资源只能被一个线程访问:CAS机制,如AtomicInterger,Atomi ...
- koa搭建nodejs项目并注册接口
使用nodejs注册接口逻辑处理会比较复杂,直接通过express或者koa能够简化开发流程,这里记录用koa来搭建nodejs项目并注册接口,对koa不太熟悉的话可以参考这一篇.让nodejs开启服 ...
- [k8s]使用私有harbor镜像源
前言 在node上手动执行命令可以正常从harbor拉取镜像,但是用k8s不行,使用kubectl describe pods xxx 提示未授权 unauthorized to access rep ...
- ATtiny88初体验(二):呼吸灯
ATtiny88初体验(二):呼吸灯 前面的"点灯"实验实现了间隔点亮/熄灭LED,但是间隔时间和亮度都没法控制,为了解决这个问题,可以使用ATtiny88的定时器模块. ATti ...
- Pandas 使用教程 JSON
目录 JSON 转换为 CSV 简单 JSON 从 URL 中读取 JSON 数据: 字典转化为 DataFrame 数据 内嵌的 JSON 数据 复杂 JSON Pandas 可以很方便的处理 JS ...
- Codeforces 1257E - The Contest
题意 三个人,每个人有一些数字,组合起来是\(1\)-\(n\),每个人可以给另一个人一个拥有的数字,问最小操作数,使得第一个人拥有\(1\)-\(i\)的数,第二个人拥有\(i+1\)-\(j\)的 ...
- Paddle图神经网络训练-PGLBox代码阅读笔记
图存储部分 paddle/fluid/framework/fleet/heter_ps graph_gpu_wrapper.h GPU图主入口 graph_gpu_ps_table.h GPU图的主要 ...