虽然SQL是为关系模型而发明的,但它对于许多形式来说都非常有效数据,包括类型异构、嵌套和无模式的文档数据。 Couchbase Capella 拥有操作引擎和分析引擎。操作和分析引擎都支持用于数据建模的 JSON 和用于查询的 SQL++。由于操作和分析用例具有不同的工作负载要求,因此 Couchbase 的两个引擎具有不同的功能,这些功能专为满足每个工作负载的要求而定制。本文重点介绍了 Couchbase 的新分析服务 Capella Columnar 服务的一些新特性和功能。
为了改进实时数据处理,Couchbase 推出了 Capella Columnar 服务。这项新服务有许多差异化技术,包括无模式数据引擎的按列存储及其处理。在本文中,我们将概述为 JSON 实现按列存储的挑战以及 柱状服务来应对这些挑战。
数据的行式和列式存储
按行存储
使用按行存储,表的每一行都存储为表数据页内的连续单元。单行的所有字段连续存储在一起,后面是下一行的字段,依此类推。每行可以有 1 到 N 列,要访问任何一列,整行都会被放入内存并进行处理。这对于事务操作非常有效,因为通常需要所有或大多数字段。
按列存储
按列存储,另一方面,存储数据的每一列(也称为 JSON 文档中的字段)分别。单列的所有值都是连续存储的。这种格式对于涉及查询多行上的几列的分析和大量读取操作特别有效,因为它可以更快地读取相关数据、更好的压缩以及更有效地使用磁盘 I/O 请求等资源。 p>
文档方式(类似于行方式)存储
集合中的每个文档都存储为连续的单元。这意味着单个文档的所有字段子字段存储在一起,后面是下一个文档的字段,依此类推。这种方法对于事务操作非常有效,因为它允许快速检索完整的文档。但是,对于仅访问多个文档中的少数字段的分析查询来说,效率可能较低,因为还会读取每个文档中不必要的数据。
使用 JSON(文档)数据模型时,与关系表中的单行相比,每个文档中通常会包含更多字段。但是,对于分析工作负载,每个查询仍然从每个文档中读取一些字段。因此,按列存储对文档存储的潜在好处甚至超过了关系数据库。但是,实施起来也不容易!在平面关系表中,模式是明确定义的,并且列的类型是先验已知的,用于存储和查询此类表的数据的技术是很好理解的。然而,为 JSON 文档存储实现按列存储引擎很困难,因为许多使 JSON 成为现代应用程序理想选择的因素却让按列存储变得困难!以下是主要原因:
- 架构灵活性
- 嵌套和复杂的结构
- 处理异构类型
- 动态架构更改
- 压缩和编码挑战
- 架构灵活性:JSON 是无架构的,这意味着每个文档都是自描述的,并且可以具有包含不同字段的不同结构。这种灵活性与传统关系数据库僵化的预定义模式形成鲜明对比。在客户文档的 JSON 模型中,在不同的子结构中,一条记录可能具有地址字段,而另一条记录可能没有,如下例所示。在列式数据库中,这种不可预测性使得定义一致的列结构变得具有挑战性。
{
“客户 ID”:101,
“名称”:“爱丽丝·史密斯”,
“电子邮件”:“alice.smith@example.com”,
“地址”: {
“街道”:“苹果街 123 号”,
“城市”:“仙境”,
“邮编”:“12345”
}
}
{
“客户 ID”:102,
“姓名”:“鲍勃·琼斯”,
“电子邮件”:“bob.jones@example.com”
// 注意:没有地址字段
}
{
“客户 ID”:103,
“姓名”:“詹姆斯·布朗”,
“电子邮件”:“james.brown@example.com”,
“地址”: {
"street": "Kenmare House, The Fossa Way",
// 县首次出现
"county": "凯里郡",
// 邮政编码而不是 zip
“邮政编码”:“V93 A0XH”,
// 国家首次出现
“国家”:“爱尔兰”
}
}
- 嵌套和复杂结构:JSON 支持任意嵌套结构,例如对象内数组内的对象。列式存储通常最适合平面表格数据,并且将这些复杂的分层结构映射到列可能非常复杂。如下例所示,单个 JSON orders 文档可能包含一个 items 列表,并且每个项目可能有自己的属性。简单地将其展平为列将具有挑战性,并且可能会导致大量稀疏列。在下面的示例中,带有 item_id = 1001 的订购商品是唯一具有适用促销代码的商品,因此在所有其他商品中都缺少该代码(类似于关系数据库中的 NULL)。李>
{
“订单id”:5002,
“客户 ID”:101,
“日期”:“2023-11-24”,
“项目”: [
{
“项目 ID”:1001,
“数量”:1,
“购买价格”:1200.00,
// 唯一具有适用促销代码的商品
“促销”:“黑色星期五”
},
{
“项目 ID”:1002,
“数量”:1,
“购买价格”:30.00
}
]
}
{
“订单id”:5003,
“客户 ID”:103,
“日期”:“2023-02-15”,
“项目”: [
{
“项目 ID”:1003,
“数量”:1,
“购买价格”:80.00
},
{
“项目 ID”:1004,
“数量”:2,
“购买价格”:300.00
},
{
“项目 ID”:1005,
“数量”:1,
“购买价格”:99.00
}
]
}
- 处理异构类型:JSON 值可以是不同的类型(例如字符串、数字、布尔值,甚至是对象和数组等复杂类型)。在不同文档的单个字段中,数据类型可能会有所不同,而列式关系数据库甚至列式文件格式通常要求列中的所有数据都具有相同的类型。如果“价格”字段有时存储为字符串(“10.99”),有时存储为数字(10.99),甚至存储为数组([10.99, 11.99])(假设存储所有季节性价格),这会使过程变得复杂从以列式格式存储此类数据的源中存储和检索数据,其中数据类型预计是统一的。
{
“项目id”:3003,
「价格」:10.99
}
{
“项目id”:3004,
“价格”:“10.99”
}
{
“项目id”:3005,
“价格”:[10.99,11.99]
}
{
“项目id”:3006,
// 价格缺失(可能表示价格不可用或未知)
}
- 动态架构更改:由于 JSON 是无架构的,因此数据结构可能会随着时间的推移而发生变化。这种灵活性是 JSON 对于现代应用程序的吸引力之一。对于架构不经常更改的列式关系数据库来说,这可能会出现问题。然而,在无模式文档存储中,添加新字段甚至更改现有字段都很简单。例如,应用程序可能开始捕获其他用户数据,例如客户地址,或将价格从一种数据类型更改为另一种数据类型。在关系列式数据库中,这需要添加一个新列,这可能是一项相对繁重的操作(尤其是在数据仓库中),或者在更改字段的数据类型的情况下甚至是不可能的。
<标题>
标题>
<正文>
之前
{
“客户 ID”:102,
“姓名”:“鲍勃·琼斯”,
“电子邮件”:“bob.jones@example.com”
// 注意:没有地址字段
}
{
“项目 ID”:4004,
“价格”:99.00
}
{
“客户 ID”:102,
“姓名”:“鲍勃·琼斯”,
“电子邮件”:“bob.jones@example.com”,
// 添加地址
“地址”: {
“城市”:“哥谭”,
"country": "神秘之地",
“邮编”:54321
}
}
{
“项目 ID”:4004,
// 价格更改为
// 数组(季节性定价)
“价格”:[99.99,109.99]
}
表>