企业级Java中的ScyllaDB:使用Eclipse JNoSQL实现高性能NoSQL
现代企业应用必须处理不断增长的数据量,同时保证低延迟、高可用性和可预测的性能。虽然关系型数据库对于事务型工作负载和复杂查询至关重要,但有些应用并不适合关系模型。管理数百万用户互动、金融事件或物联网(IoT)数据的系统,通常需要一种面向分布式环境和水平扩展能力构建的持久化方案。在这些场景下,ScyllaDB 为企业架构师提供了一个颇具吸引力的选择。
ScyllaDB 是一种基于宽列数据模型的高性能分布式 NoSQL 数据库。它受 Apache Cassandra 启发,并兼容其查询语言(CQL)。ScyllaDB 采用每核分片架构,以最大化硬件效率,并在密集工作负载下保持一致性能。何时应选择 ScyllaDB,而不是传统关系型数据库或其他 NoSQL 选项?本文考察其列族模型,将其与 Cassandra 进行比较,并展示如何将 ScyllaDB 集成到 Java 企业应用中。
现代企业应用需要的不仅仅是基本的数据存储。它们需要高吞吐量、低延迟和水平扩展能力,以管理复杂的工作负载。ScyllaDB 是一种基于宽列数据模型的分布式高性能 NoSQL 数据库,专为需要大规模可预测性能的应用而设计。Discord、Expedia 和 Comcast 等公司在真实场景中使用 ScyllaDB,包括消息平台、实时应用和高容量数据处理。ScyllaDB 尤其适合事件驱动架构、物联网平台、金融系统以及具有严苛读写负载的应用。
ScyllaDB 使用基于模式的方法和 Cassandra 查询语言(CQL),其语法类似 SQL,包括 CREATE TABLE、INSERT 和 SELECT 等语句。除基本类型外,ScyllaDB 还支持 List、Set 和 Map 等集合,以及用于复杂数据建模的用户定义类型(UDT)。这种灵活性伴随着权衡:与关系型数据库不同,ScyllaDB 使用查询驱动的数据建模,限制对任意字段的查询,并要求谨慎使用二级索引。本文将考察 ScyllaDB 的列族架构,将其与 Apache Cassandra 进行比较,回顾其能力和限制,并展示如何将 ScyllaDB 集成到企业 Java 应用中。
使用 Eclipse JNoSQL 设置 ScyllaDB
ScyllaDB 支持部署在云环境、本地数据中心和本地开发环境中。团队可以根据基础设施需求选择自管理或托管云服务。在本教程中,我们将使用 Docker 运行单个 ScyllaDB 实例,以聚焦数据模型和 Java 集成,避免分布式集群的复杂性。
要在本地启动 ScyllaDB,请执行以下命令:
此命令启动一个 ScyllaDB 容器并暴露 9042 端口,这是 Cassandra 查询语言(CQL)连接的默认端口。初始化完成后,数据库即可接受应用请求。
将 ScyllaDB 与 Eclipse JNoSQL 集成
接下来,确定 Java 应用如何连接 ScyllaDB。从 Eclipse JNoSQL 1.1.19 开始,专用驱动提供原生 ScyllaDB 支持,使标准操作与 ScyllaDB 特定功能可以并存。在本示例中,我们使用一个结合 Eclipse MicroProfile Config、CDI 和 Jakarta JSON Processing 的 Java 应用。这些技术在 MicroProfile 和 Jakarta EE 环境中很常见,有助于集成。
首先,将 ScyllaDB 依赖添加到 Maven 项目中:
将 ${jnosql.version} 属性设置为 Eclipse JNoSQL 1.1.19 或兼容的更新版本。
配置数据库模式
ScyllaDB 使用类似 Apache Cassandra 的基于模式的方法。开发者通过 CQL 显式定义键空间、表、列类型和用户定义类型。在此示例中,我们创建一个名为 developers 的键空间、两个分别用于人员和电影的表,以及若干用户定义类型。Eclipse JNoSQL 支持通过 MicroProfile Config 提供初始化查询。将以下属性添加到 META-INF/microprofile-config.properties:
此配置突出了多个关键点。CQL 类似 SQL,使用 CREATE 等语句、显式类型和主键。ScyllaDB 支持 list 和 set 等集合,使你无需额外表就能表示多个值。用户定义类型(UDT)将相关属性分组为结构化对象,例如 director 类型。Movie 表使用了 FROZEN<director>。在 CQL 中,FROZEN 将 UDT 作为单个值存储,防止单独修改各个字段。我们还为 age 列创建了二级索引。尽管 ScyllaDB 支持二级索引,但其有效性取决于工作负载、基数和查询类型。不要将它们等同于关系型数据库索引,也不要将其视为合理分区键设计的替代品。
通过应用配置定义数据库模式适用于教程,但不建议用于生产环境。应使用受版本控制的迁移工具(例如 Liquibase,它通过 Cassandra 扩展支持 ScyllaDB)单独管理模式演进。这种方法将应用部署与模式变更分离,并改善对数据库更新的控制。
映射实体和用户定义类型
定义数据库模式后,我们可以创建 Java 模型。首先创建 Movie,它表示一个 ScyllaDB 实体;再创建 Director,它表示一个用户定义类型。
Movie 实体使用标准的 Jakarta NoSQL 映射方式,通过 @Entity、@Id 和 @Column 进行映射。director 属性通过 @Column(udt = "director") 映射到 ScyllaDB 用户定义类型。在序列化和反序列化期间,Eclipse JNoSQL 使用 ScyllaDB 驱动在 Java 对象与其 CQL UDT 表示之间进行转换。UDT 类似于将相关字段分组的结构化 JSON 对象,但它是模式中定义的强类型 CQL 值。Director 中的 Set<String> 字段展示了 ScyllaDB 对集合的原生支持,使你无需单独的关系表就能为导演的电影标题建模。接下来,我们定义 Person 实体:
phones 属性展示了 ScyllaDB 支持的另一种集合类型:List<String>。Eclipse JNoSQL 将 Java List<String> 直接映射到对应的 CQL 集合,无需额外转换器或自定义序列化。这些示例展示了 ScyllaDB 如何将基于模式的模型与对结构化类型和集合的支持结合起来。Eclipse JNoSQL 提供熟悉的映射注解,使应用能够使用 ScyllaDB 的原生特性。然而,对象映射的便利性并不能取代查询驱动数据建模的需要。在 ScyllaDB 这样的分布式数据库中,分区键、访问模式和数据分布仍然是重要的架构考量。
运行应用程序:Jakarta NoSQL 与 Jakarta Data
配置数据库并映射实体后,下一步是在 Java 中与 ScyllaDB 交互。Eclipse JNoSQL 提供两种互补方式:Jakarta NoSQL,一种面向 NoSQL 数据库的基于模板的 API;以及 Jakarta Data,它使用仓储模式。两者都与 CDI 集成,使开发者可以将持久化组件注入运行在带 CDI 的 Java SE 或 Jakarta EE 应用服务器上的应用。
Jakarta NoSQL:模板方式
第一种方式使用 Jakarta NoSQL 的 ColumnTemplate 接口,为宽列数据库提供一致的 API。开发者可以插入、更新、删除和检索实体,而无需直接与数据库驱动交互。
例如,你可以创建一个 Person 实体,插入它,并使用流式查询 API 检索它:
实体映射会自动处理 Java List<String> 及其对应的 CQL 集合,因此你不需要额外的序列化代码来持久化电话号码。Template 方式适用于开发者需要对持久化操作进行显式控制、希望以编程方式构建查询,或偏好跨 NoSQL 实现使用一致 API 的场景。
Jakarta Data:仓储方式
第二种方式使用 Jakarta Data,并应用仓储模式来简化持久化。开发者定义一个接口,由 Eclipse JNoSQL 提供实现。以我们的 PersonRepository 为例:
该接口提供标准的 CRUD 操作,例如 save、findById 和 deleteById。仓储可以通过 CDI 注入,并直接在应用中使用:
Jakarta Data 还支持从方法名派生的查询方法,使开发者无需编写实现即可表达查询。MovieRepository 展示了这一能力,并通过其专用接口暴露 ScyllaDB 特定功能:
findByAge 方法展示了查询派生,而 @CQL 注解允许执行原生 Cassandra 查询语言语句。在此示例中,age 列具有二级索引,因此你可以不使用分区键按年龄查询电影。然而,查询派生并不能消除 ScyllaDB 的查询限制。仓储方法必须与模式和索引支持的查询相匹配。检索所有记录对于演示很有用,但在生产环境中处理大型数据集时应谨慎使用。
超越标准 CRUD 操作
尽管 Jakarta NoSQL 和 Jakarta Data 简化了常见持久化操作,但 ScyllaDB 集成并不限于标准仓储和模板。Eclipse JNoSQL 提供 ScyllaDBTemplate,这是一个专用实现,在保持熟悉 Java 编程模型的同时暴露数据库特定功能。借助这种集成,开发者可以:
- 控制一致性级别:指定 Cassandra 兼容的一致性级别,例如
ConsistencyLevel.ONE,以平衡一致性、延迟和可用性。 - 执行原生 CQL:当标准抽象不足时,直接运行数据库特定查询,包括使用 ScyllaDB 特定功能的查询。
- 处理结构化数据:映射
List和Set等集合以及用户定义类型,无需手动将 Java 对象转换为 CQL 值。 - 选择合适的抽象:使用供应商中立的
ColumnTemplate进行通用持久化,或使用ScyllaDBTemplate获得额外的数据库控制。
例如,专用模板支持如下操作:scyllaDBTemplate.save(person, ConsistencyLevel.ONE);
Eclipse JNoSQL 的一个关键架构特性是:抽象并不意味着失去对数据库特定能力的访问。开发者可以针对常见操作使用标准化 API,同时在需要时仍能访问原生功能。Jakarta NoSQL 和 Jakarta Data 相互补充,提供组织持久化逻辑的不同方式。Template 方式提供显式、灵活的交互,而 Repository 方式强调声明式数据访问和关注点分离。两者都受益于 CDI 集成,允许你在独立 Java 应用、微服务或 Jakarta EE 环境中使用相同的持久化组件。选择取决于应用的架构需求,但底层 ScyllaDB 能力和数据建模约束保持不变。
结论
对于企业架构师而言,ScyllaDB 是一个强有力的选择,尤其适用于要求高吞吐量、稳定延迟和水平扩展能力的应用。其基于模式的宽列模型、对集合和用户定义类型的支持,以及对 Cassandra 查询语言的兼容性,为分布式数据持久化提供了熟悉且稳健的解决方案。然而,要做出明智的架构决策,理解其查询限制、二级索引和数据建模权衡非常重要。
来源:https://github.com/JNOSQL/demos-se#scylla。DZone 贡献者表达的观点仅代表其本人。