数据库的二级索引

在这里插入图片描述

二级索引

10.1 二级索引作为额外的键

表结构

正如第8章提到的,二级索引本质上是包含主键的额外键值对。每个索引通过B+树中的键前缀来区分。

type TableDef struct {// 用户定义的部分Name    stringTypes   []uint32 // 列类型Cols    []string // 列名Indexes [][]string // 第一个索引是主键// 自动分配的B+树键前缀,用于不同的表和索引Prefixes []uint32
}

第一个索引被用作主键,因为它本身也是一个索引。

键值结构

对于二级索引,我们可以将主键存储在B+树的值中,以便通过它找到完整的行数据。然而,与主键不同的是,二级索引通常没有唯一性约束,因此可能会出现重复的B+树键。

为了避免修改B+树以支持重复键,我们可以将主键附加到B+树的键上,使其唯一,并将值留空。

例如:

create table t1 (k1 string,k2 int,v1 string,v2 string,primary key (k1, k2),index idx1 (v1),index idx2 (v2, k2)
);

对应的KV结构如下:

  • 主表 t1:
    • 键:prefix1, k1, k2
    • 值:v1, v2
  • 索引 idx1:
    • 键:prefix2, v1, k1, k2
    • 值:(empty)
  • 索引 idx2:
    • 键:prefix3, v2, k2, k1
    • 值:(empty)

10.2 使用二级索引

通过匹配列选择索引

为了执行范围查询,我们必须选择一个与查询键匹配的索引。这个信息存储在 Scanner 类型中,以便 Scanner.Deref() 可以使用它。

type Scanner struct {// 范围,从 Key1 到 Key2Cmp1 int // CMP_??Cmp2 intKey1 RecordKey2 Record// 内部字段db     *DBtdef   *TableDefindex  int // 使用哪个索引?iter   *BIter // 底层的B+树迭代器keyEnd []byte // 编码后的 Key2
}

索引可以由多列组成。例如,索引 (a, b) 可以服务于查询 (a, b) > (1, 2)。它也可以服务于查询 a > 1,因为这等价于 (a, b) > (1, +∞)。索引的选择仅仅是通过匹配列完成的。

func dbScan(db *DB, tdef *TableDef, req *Scanner) error {// ...isCovered := func(index []string) bool {key := req.Key1.Colsreturn len(index) >= len(key) && slices.Equal(index[:len(key)], key)}req.index = slices.IndexFunc(tdef.Indexes, isCovered)// ...
}
将缺失列编码为无穷大

在最后一个例子中,查询 a > 1 使用索引 (a, b),但只使用了其中的一列,因此我们需要将剩余的列编码为无穷大。

输入查询使用索引
a > 1(a, b) > (1, +∞)
a ≤ 1(a, b) < (1, +∞)
a ≥ 1(a, b) > (1, -∞)
a < 1(a, b) < (1, -∞)

这可以通过修改我们的保持顺序编码实现。我们选择 \xff 表示正无穷大(+∞),而空字符串表示负无穷大(-∞)。由于没有列会被编码为空字符串,因此在 -∞ 的情况下可以直接忽略缺失的列。而在 +∞ 的情况下,我们需要在每个编码列前添加一个标签,以确保它们不会以 \xff 开头。

// 保持顺序的编码
func encodeValues(out []byte, vals []Value) []byte {for _, v := range vals {out = append(out, byte(v.Type)) // 添加标签:不以 0xff 开头switch v.Type {case TYPE_INT64:var buf [8]byteu := uint64(v.I64) + (1 << 63)binary.BigEndian.PutUint64(buf[:], u) // 大端out = append(out, buf[:]...)case TYPE_BYTES:out = append(out, escapeString(v.Str)...)out = append(out, 0) // 空字符终止default:panic("what?")}}return out
}

我们在每个编码列前添加列类型代码作为标签。这不仅方便调试,还可以通过查看十六进制转储直接解码内容。

这是一个小步骤,用于支持前缀列上的范围查询。

// 用于主键和索引
func encodeKey(out []byte, prefix uint32, vals []Value) []byte {// 4字节的表前缀var buf [4]bytebinary.BigEndian.PutUint32(buf[:], prefix)out = append(out, buf[:]...)// 保持顺序编码的键out = encodeValues(out, vals)return out
}// 用于输入范围,可能是索引键的前缀
func encodeKeyPartial(out []byte, prefix uint32, vals []Value, cmp int,
) []byte {out = encodeKey(out, prefix, vals)if cmp == CMP_GT || cmp == CMP_LE { // 编码缺失列为无穷大out = append(out, 0xff) // 不可达的 +∞} // 否则:-∞ 是空字符串return out
}

10.3 维护二级索引

与主数据同步

更新可能涉及多个带有二级索引的B+树键。当一行被更改时,我们必须删除旧的索引键并插入新的索引键。为此,扩展了B+树接口以返回旧值。

type UpdateReq struct {tree *BTree// 输出Added    bool // 添加了新键Updated  bool // 添加了新键或旧键被更改Old      []byte // 更新前的值// 输入Key []byteVal []byteMode int
}

使用新的信息:

func dbUpdate(db *DB, tdef *TableDef, rec Record, mode int) (bool, error) {// ...// 插入行req := UpdateReq{Key: key, Val: val, Mode: mode}if _, err = db.kv.Update(&req); err != nil {return false, err}// 维护二级索引if req.Updated && !req.Added {// 使用 `req.Old` 删除旧的索引键...}if req.Updated {// 添加新的索引键...}return req.Updated, nil
}
多键更新的原子性

原子性不可组合!即使单个KV操作是原子的,当涉及多个键时,我们也会失去原子性。如果数据库崩溃或在更新二级索引时发生错误,应该回滚到之前的状态。

仅使用 get, set, del 实现这一点非常棘手,这就是为什么简单的KV接口有很大的局限性。下一步是引入事务性KV接口,以允许对多个键甚至并发读取进行原子操作。


10.4 表和索引的总结

  • 行和列作为KV对
  • 范围查询
    • B+树迭代器。
    • 保持顺序的编码。
  • 二级索引
    • 索引选择。
    • 需要一个事务性接口。

代码仓库地址:database-go

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

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

相关文章

Java IO流核心处理方式详解

一、IO流概述 Java IO&#xff08;Input/Output&#xff09;流是处理输入输出操作的核心机制&#xff0c;通过流&#xff08;Stream&#xff09;的形式实现设备间的数据传输。所有操作都基于以下两个核心抽象&#xff1a; InputStream/OutputStream&#xff1a;字节流基类 Re…

WidowX-250s 机械臂的简单数字孪生案例

前面一段时间记录了一下WidowX-250s机械臂的学习与遥操作演示&#xff0c;相关链接如下&#xff1a; WidowX-250s 机械臂学习记录&#xff1a; https://blog.csdn.net/qq_54900679/article/details/145556979 WidowX-250s 机械臂遥操作演示记录&#xff1a; https://blog.c…

uniapp 云开发全集 云开发的概念

一、云开发的概念 1.1 云开发介绍 云开发 unicloud 是 DCloud 联合阿里云、腾讯云、支付宝云&#xff0c;为开发者提供的基于 serverless 模式和 js 编程的云开发平台&#xff0c;可以使用极小的成本代价开发具轻松实现前后台整体业务。 1.2 云开发的核心组成 云开发的核心组…

GGD独立站的优势

GGD模式(基于Google生态的独立站模式)越来越受欢迎&#xff0c;主要有以下原因&#xff1a; 1. 全球化覆盖 GGD独立站依托Google强大的生态系统&#xff0c;能够帮助企业轻松触达全球用户&#xff0c;实现国际化布局&#xff0c;拓展业务范围。Google作为全球最大的搜索引擎&…

签名去背景图像处理实例

一、前言 在生活中我们经常用到电子签名&#xff0c;但有时候我们所获取的图像的彩色图像&#xff0c;我们需要获取白底黑字的电子签名&#xff0c;我们可以通过下面程序对彩色图像进行处理达到我们的处理目的。 原始彩色图像如下&#xff1a; 二、程序和运行结果 clear all;c…

WebAssembly(Wasm):现代Web开发的超级加速器

在当今的Web开发领域&#xff0c;性能和效率是开发者们永恒的追求目标。随着Web应用的复杂度不断增加&#xff0c;传统的JavaScript在某些场景下已经难以满足高性能计算和复杂逻辑处理的需求。此时&#xff0c;WebAssembly&#xff08;Wasm&#xff09;作为一种新兴的Web技术&a…

简单理解MCP:AI如何使用工具

简单理解MCP&#xff1a;AI如何使用工具&#xff08;以天气/新闻服务为例&#xff09; 你是否注意到人工智能(AI)助手正变得越来越智能&#xff1f;它们不再仅仅是聊天&#xff0c;还能执行实际操作&#xff0c;比如查询天气、在线搜索&#xff0c;甚至预订会议。这通常涉及到…

护网奇谈: 红队工程师手记

零、引言&#xff1a;在演练中活着&#xff0c;在现实中消失 人们常说&#xff0c;护网是网络安全界的“大阅兵”。 每年一次&#xff0c;红蓝对阵&#xff0c;政企联动&#xff0c;战鼓擂响&#xff0c;态势大屏高挂&#xff0c;PPT如潮&#xff0c;报告成山。 你在屏幕前看…

机器翻译与数据集

机器翻译与数据集 语言模型是自然语言处理的关键&#xff0c;而机器翻译是语言模型最成功的基准测试。因为机器翻译正是将输入序列转换成输出序列的序列转换模型&#xff08;sequence transduction&#xff09;的核心问题。序列转换模型在各类现代人工智能应用中发挥着至关重要…

基于 HTML 和 CSS 实现的 3D 翻转卡片效果

一、引言 在网页设计中&#xff0c;为了增加用户的交互体验和视觉吸引力&#xff0c;常常会运用一些独特的效果。本文将详细介绍一个基于 HTML 和 CSS 实现的 3D 翻转卡片效果&#xff0c;通过对代码的剖析&#xff0c;让你了解如何创建一个具有立体感的卡片&#xff0c;在鼠标…

C++ 中二级指针的正确释放方法

C 中二级指针的正确释放 一、什么是二级指针&#xff1f; 简单说&#xff0c;二级指针就是指向指针的指针。 即&#xff1a; int** p;它可以指向一个 int*&#xff0c;而 int* 又指向一个 int 类型的变量。 常见应用场景 动态二维数组&#xff08;例如 int** matrix&#x…

大数据平台与数据仓库的核心差异是什么?

随着数据量呈指数级增长&#xff0c;企业面临着如何有效管理、存储和分析这些数据的挑战。 大数据平台和 数据仓库作为两种主流的数据管理工具&#xff0c;常常让企业在选型时感到困惑&#xff0c;它们之间的界限似乎越来越模糊&#xff0c;功能也有所重叠。本文旨在厘清这两种…

Winform(11.案例讲解1)

今天写两个案例,用于更好的理解控件的使用 在写之前先写一个类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _1.案例讲解 { internal class Student { public string …

Spring AMQP源码解析

目录 channel和connection的区别 自动装配RabbitAutoConfiguration 消息发送流程 获取connection对象 获取channel对象 AMQConnection读取frame帧并回调publishconfirm和publishreturn MainLoop线程监听 执行回调 channel和connection的区别 Spring AMQP 是 Spring 框…

Linux系统安装PaddleDetection

一、安装cuda 1. 查看设备 先输入nvidia-smi&#xff0c;查看设备支持的最大cuda版本&#xff0c;选择官网中支持的cuda版本 https://www.paddlepaddle.org.cn/install/quick?docurl/documentation/docs/zh/install/conda/linux-conda.html 2. 下载CUDA并安装 使用快捷键…

Linux系统中的时间同步服务

1.时间同步&#xff1a;多主机协作工作&#xff0c;时间应该保持一致&#xff0c;如加密协议、日志、集群等&#xff0c;利用NTP&#xff08;Network Time Protocol&#xff09;协议使得各个主机时间达到同步。 ntp:将系统时钟和世界协调时UTC同步&#xff0c;精度在局域网内可…

【Linux笔记】系统的延迟任务、定时任务极其相关命令(at、crontab极其黑白名单等)

一、延时任务 1、概念 延时任务&#xff08;Delayed Jobs&#xff09;通常指在指定时间或特定条件满足后执行的任务。常见的实现方式包括 at 和 batch 命令&#xff0c;以及结合 cron 的调度功能。 2、命令 延时任务的命令最常用的是at命令&#xff0c;第二大节会详细介绍。…

软考 系统架构设计师系列知识点 —— 黑盒测试与白盒测试(1)

本文内容参考&#xff1a; 黑盒测试和白盒测试详解-CSDN博客 软件测试中的各种覆盖&#xff08;Coverage&#xff09;详解-CSDN博客 特此致谢&#xff01; 零、概述 黑盒测试又名为功能测试&#xff0c;主要目的是发现软件设计的需求或者是软件设计规格说明书中的错误缺陷。…

yolov11 epoch100轮 训练笔记5 kaggle comet

Football Players Detection using YOLOV11 | Kaggle !pip install comet_ml import comet_mlcomet_ml.login(project_name"c") Comet - Build Better Models Faster yolov11训练 100轮一眨眼训练完了 然而comet接不到yolo的sdk 优秀 训练17轮map就0.99了 v5训练100…

Ubuntu K8S(1.28.2) 节点/etc/kubernetes/manifests 不存在

Ubuntu K8S(1.28.2) 节点/etc/kubernetes/manifests 不存在 在查看日志&#xff08;journalctl -xefu kubelet&#xff09;时发现各节点/etc/kubernetes/manifests 不存在&#xff0c;但主节点没有异常 21080 file.go:104] "Unable to read config path" err"…