Aerospike:入门与实战——开发你的第一个 Aerospike 应用程序

558 阅读29分钟

现在你已经了解了 Aerospike 的基础知识,接下来让我们开始编写第一个使用 Aerospike 作为数据库的程序。Aerospike 支持多种编程语言,如 Java、C、C#、Python 和 Node.js,但本书将重点关注 Java 和 Python。如果你跟随示例操作,需要注意的是 Python 客户端支持在 Mac 和 Linux 上运行,但不支持 Windows。不过,书中讨论的概念适用于 Aerospike 支持的所有编程语言。关于支持的编程语言、各客户端版本与 Aerospike 服务器的兼容情况以及支持的功能,请参阅 Aerospike 客户端矩阵。

安装 Aerospike

在安装 Aerospike 时,需要安装两个组件。第一个是实际的 Aerospike 数据库,第二个是用于与其交互的工具。数据库安装包仅在 Linux 系统中包含这些工具。如果你使用的是 Mac OS,并希望避免在每次运行工具包中的命令前调用 Docker,则需要本地版本的工具。

由于 Aerospike 设计用于 Linux 操作系统,因此无法在 Windows 或 Mac OS 上原生运行。不过,有一些简单的方法可以在这些设备上运行 Aerospike。

在 Windows 或 Mac 上安装 Aerospike

要在 Windows 或 Mac 上运行数据库,需要使用 Docker 或 VirtualBox 等虚拟化层来运行 Linux。Docker 或类似的容器化方案启动起来相对更简单,所以我们将以此为重点。(如果你使用的是像 VirtualBox 这样的完整虚拟化层,则可以按照 Linux 安装的步骤进行。)

你可以使用 Docker Desktop,目前它对于个人使用是免费的。然而,许多开发者和组织出于许可证的考虑转而选择免费的开源替代方案 Podman。Podman 可以配置为 Docker 的替代品,并且拥有友好的用户界面 Podman Desktop,因此本章将重点介绍 Podman。

要安装 Podman Desktop,请按照 Podman 网站上的 Mac 或 Windows 安装指南进行操作。启动软件后,系统会提示你安装 Podman 引擎,这是必要的步骤。

在用户界面中,你需要启用 Docker 兼容模式。此功能允许你使用 Docker 命令,并让 Podman 处理这些命令。要启用此设置,请在 Podman Desktop 中转到设置(窗口左下角的齿轮图标),选择“Preferences”,然后在右侧的“Extension: Podman”下找到“Docker Compatibility”,或在“Preferences”搜索栏中输入“Docker”。启用此设置后重启 Podman Desktop,你会注意到“Docker Compatibility”出现在屏幕左下角。如图 2-1 所示。

你需要安装 Docker CLI 才能在 Podman 中使用 Docker 命令。在 Mac 上,可以通过 Homebrew 使用以下命令安装:

brew install docker

在 Windows 上,你需要安装 Docker Desktop 才能访问 Docker CLI。截至写作时,使用 Docker CLI 结合 Podman 运行容器在 Docker Desktop 许可证下是允许的,不需要商业许可证。

image.png

注意

如果你已经安装并使用过 Docker,可能会遇到 Docker 和 Podman 之间的冲突。例如,你可能会看到类似以下的错误:

$ docker ps
Cannot connect to the Docker daemon at
unix:///Users/albert/.docker/run/docker.sock. 
Is the docker daemon running?

可以通过删除 ~/.docker 文件夹来解决此问题:

$ podman machine stop
$ rm -rf ~/.docker/
$ podman machine start
Starting machine "podman-machine-default"
API forwarding listening on: /var/run/docker.sock

安装 Aerospike 数据库容器

安装 Podman 和 Docker CLI 后,可以使用它来拉取最新的 Aerospike 数据库并安装。Aerospike 有两个版本:完全开源的社区版(Community Edition)和企业版(Enterprise Edition)。企业版包括社区版的所有功能,还提供企业级功能,如静态加密、压缩、持久删除等。Aerospike 企业版可免费下载并用于单节点数据库,但集群超过一个节点需要商业许可证。社区版支持多节点集群且免费,但缺少企业版的一些功能。

为了展示完整的功能,本书的编程部分将使用 Aerospike 企业版,但两个版本都可以使用,对于仅企业版支持的功能,我们会在讨论中做出区分。如果你想了解社区版,可以在 Aerospike 社区版官网找到安装说明和下载。

要获取最新的 Docker 镜像并启动 Aerospike 服务器,运行以下命令:

docker run -tid --name aerospike -p 3000-3002:3000-3002 aerospike/aerospike-server-enterprise

此命令将下载 Aerospike 数据库的最新版本并启动一个容器。以下是 Docker 命令的参数说明:

  • run:指示 Docker 启动一个程序。
  • -tid:表示程序应以分离和交互模式运行,这样可以在以后附加到进程并查看日志。
  • --name:为 Docker 进程指定一个名称,以便可以使用此名称通过 Docker 管理进程,而无需使用容器 ID。
  • -p 3000-3002:3000-3002:将容器的 3000、3001 和 3002 端口映射到底层主机上的相应端口。这意味着可以连接数据库,无需担心数据库在容器中运行。端口 3000 是客户端与服务器通信的端口,对开发者来说最为重要。端口 3001 用于“fabric”——集群中节点之间的数据交换。端口 3002 用于心跳检测,以便集群知道节点或节点组是否响应。
  • aerospike/aerospike-server-enterprise:要下载的镜像名称,在这里是 Aerospike 企业版。撰写时最新版本为 6.4.0.1。

为确保成功运行,执行以下命令:

% docker ps

这将显示类似如下的内容:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
979765d925df aerospike/aerospike-server-enterprise "/usr/bin/as-tini-st…" 
2 minutes ago Up 2 minutes 0.0.0.0:3000-3002->3000-3002/tcp,
:::3000-3002->3000-3002/tcp aerospike

在你的 Podman Desktop 中,你也应该能够看到名为 aerospike 的容器正在运行。

安装工具

在 Mac 上本地运行工具可以免去通过 docker exec 运行每个命令的步骤,使命令更简洁。可以从 Aerospike 网站的下载页面直接下载本地版本工具,用户界面也很直观。

在“System”下拉菜单中选择“macOS”,并确保在“CPU”下拉菜单中选择与您的 Mac 匹配的选项——Intel/AMD64 适用于基于 Intel 的 Mac,而 ARM64 则适用于基于“M”系列处理器的 Mac。

下载完成后,运行二进制文件以开始安装流程。你可以通过启动终端窗口并执行以下命令来验证安装是否成功:

% asinfo -v build
6.4.0.1

请注意,输出的版本号可能会有所不同,具体取决于你安装的版本,但只要有版本信息返回,就表示安装成功。

Aerospike 工具无法在 Windows 上原生运行,因此对于 Windows 用户来说,最佳选择是通过容器运行这些工具。你启动的 Aerospike 容器已经安装了工具,可以在容器内使用工具,方法如下:

% docker exec -it aerospike 'asinfo -v build'
6.4.0.1

在 Linux 上安装 Aerospike

由于 Aerospike 可以在 Linux 上原生运行,因此在该平台上安装非常简单。使用浏览器进入 Aerospike 下载页面,在此你会看到可供下载的 Aerospike 版本列表,如图 2-2 所示。选择正确的版本,然后下载与你的系统相对应的文件。你可以选择直接下载文件,或者复制文件的链接,以便使用 wget 等工具进行下载。

image.png

下载服务器后,解压文件。进入解压后生成的相应子目录。最后,运行以下命令:

% sudo ./asinstall

这将把 Aerospike 和 Aerospike 工具安装到系统中的适当文件夹。

在 Linux 上,Aerospike 作为服务安装,便于系统维护。根据你的 Linux 版本,可以使用 servicesystemctl 命令检查服务状态并在需要时启动:

% sudo systemctl status aerospike
% sudo systemctl start aerospike

使用 AeroLab 简化部署

如果你运行在 Docker/Podman 等虚拟环境中,或 AWS 或 Google Cloud 等云环境中,开源工具 AeroLab 可以简化不同 Aerospike 环境的部署。

安装和配置 AeroLab

进入 AeroLab 的 GitHub 页面,选择所需版本,在“Assets”下找到你的操作系统对应的安装包,下载并安装。

安装 AeroLab 后,首先指定要使用的后端。例如,如果你使用 Podman,可以使用以下命令:

aerolab config backend -t docker

AeroLab 支持上下文相关的帮助功能,因此如果在任何步骤遇到问题,只需在要查询的内容后加上“help”即可。例如,如果想配置后端但不清楚选项,可以输入:

aerolab config backend help

AeroLab 会显示相应的帮助信息:

% aerolab config backend help
Usage:
  aerolab [OPTIONS] config backend [backend-OPTIONS] [help]
Global Options:
--beep                 退出时使终端蜂鸣;指定多次时,成功一次蜂鸣,失败多次蜂鸣
--beepf                类似 beep,但仅在失败时触发蜂鸣,成功时不蜂鸣
[backend command options]
-t, --type=            支持的后端:aws|docker|gcp
-p, --key-path=        AWS 和 GCP 后端:指定用于存储 SSH 密钥的路径,默认:${HOME}/aerolab-keys/ (默认值:${HOME}/aerolab-keys/)
-r, --region=          AWS 后端:覆盖默认的 AWS 配置区域
-P, --aws-profile=     AWS 后端:提供要使用的配置文件;设置此项将忽略 AWS_PROFILE 环境变量
    --aws-nopublic-ip  AWS 后端:如果设置,aerolab 将不请求公共 IP,仅使用私有 IP
-o, --project=         GCP 后端:覆盖默认的 GCP 配置项目
-a, --docker-arch=     设置为 amd64 或 arm64 以强制 Docker 使用特定架构;参见 https://github.com/aerospike/aerolab/tree/master/docs/docker_multiarch.md
-d, --temp-dir=        使用非默认的临时目录
Available commands:
  help 打印帮助

使用 AeroLab

配置 AeroLab 后,可以使用它创建集群。最简单的方法是:

aerolab cluster create

此命令会从公开版本中找到最新的 Aerospike 企业版,下载镜像,基于此镜像创建容器并启动 Aerospike。如果在 Podman Desktop 窗口查看或在命令提示符下使用 docker ps,你应该会看到一个名为 aerolab-mydc_1 的进程。AeroLab 集群有名称,通常使用 -n <cluster_name> 参数指定集群名称。由于上述命令省略了该参数,AeroLab 默认为 mydc

注意

AeroLab 会在 Docker/Podman 中创建集群,并将它们的端口暴露给主机,以便你可以通过 IDE 连接到它们。默认情况下,Aerospike 监听 3000 端口,但 AeroLab 创建的容器端口从 3100 开始。如果你要连接由 AeroLab 启动的集群,请确保连接集群时更改端口,如本章稍后所示。

因此,不再使用:

IAerospikeClient client = new AerospikeClient(null, "127.0.0.1", 3000);

而改用:

IAerospikeClient client = new AerospikeClient(null, "127.0.0.1", 3100);

要查看集群,可以运行:

aerolab cluster list

这将显示一组信息,结果中一些最相关的列包括:

CLUSTERS
ClusterName ... ExposedPort ... AsdVer Arch Distro DistroVer ...
 mydc       ... 3100        ... 6.4.0.5 arm64 ubuntu 22.04   ...

你可以看到,集群名称为 mydc(AeroLab 默认值),节点监听端口为 3100,运行的是 Aerospike 版本 6.4.0.5,操作系统为 Ubuntu 22.04。

如示例所示,可以轻松使用 Docker/Podman 或 AeroLab 部署单节点集群。然而,使用 AeroLab 命令更易于记忆,并且在其他部署场景中更具灵活性。例如,假设你想创建一个名为 source 的五节点 Aerospike 社区版 6.4.0 集群,可以使用以下命令实现:

aerolab cluster create -n source -c 5 -v 6.4.0c
  • -n source 告诉 AeroLab 集群名称应为 source(而不是 mydc)。
  • -c 5 指定节点数量为五个。
  • -v 6.4.0c 表示使用 Aerospike 社区版 6.4.0。请注意,版本号后的 c 表示选择社区版;如果省略此项,则默认选择企业版。

完成该命令后,你将拥有一个五节点的 Aerospike 集群。使用 Docker/Podman 实现相同的设置将需要大量配置,以确保容器形成一个分布式的 Aerospike 集群并正确通信。

要连接到在 Docker 中运行的多节点 AeroLab 集群,需要传递一个“服务替代”标志:

ClientPolicy cp = new ClientPolicy();
cp.useServicesAlternate = true;
IAerospikeClient client = new AerospikeClient(cp, "127.0.0.1", 3100);

这是因为组成集群的容器之间的网络通信在 Docker 内部使用了主机不可见的内部地址。指定 useServicesAlternate 告诉客户端在它可见的网络端口上进行通信。更多信息请参阅 Aerospike 文档。

AeroLab 提供了许多有助于 Aerospike 部署的功能,其中一个非常有用的命令是连接到集群中的某个节点,可以通过以下命令完成:

aerolab attach shell [-n cluster_name]

与大多数 AeroLab 命令一样,此命令需要集群名称,默认名称为 mydc。假设你在本章前面创建了一个名为 mydc 的单节点集群,可以运行以下命令获取该服务器上的 shell:

aerolab attach shell
root@mydc-1:/#

请注意,如果你的集群中有多个节点,例如前面创建的 source 集群,可以使用 --node(或 -l)选项选择要运行 shell 的节点。和往常一样,你可以在命令末尾加上 help 查看命令的所有选项,例如 aerolab attach shell help。要连接到 source 集群中的第三个节点,可以执行:

aerolab attach shell -n source --node 3
root@source-3:/#

Aerospike 术语

Aerospike 有一些与其他数据库类似但不完全相同的概念。为了理解这些概念,让我们将 Aerospike 与关系型数据库管理系统(RDBMS)进行对比。

在图 2-3 中,你可以看到关系型行式数据库的典型结构。数据分组存储在数据库中,每个数据库彼此逻辑上独立。每个数据库包含表,表是行(也称为记录)的逻辑分组,例如客户或账户。

image.png

在关系型数据库管理系统(RDBMS)中,表具有模式,因此表中的每一行都具有相同的列。该模式定义了应存在的列、列的数据类型、是否允许空值等。

行包含数据,数据分解为列,每列包含一个独立的信息。由于模式要求所有行具有相同的结构,因此即使某条数据不需要,仍然需要在行中保留一个空值。例如,图 2-3 中的 Joe 的行没有“updated_at”条目,因此该值将为 null。

相比之下,Aerospike 的数据模型如图 2-4 所示。

image.png

最高级的存储概念是命名空间(namespace),它是 Aerospike 处理的命名对象的逻辑组织。在这种情况下,命名空间的功能类似于关系型概念中的表空间或数据库。它定义了用于存储数据和索引的存储空间,集群中数据副本的数量等。命名空间通常彼此隔离,这样如果不同的命名空间将数据存储在不同的 SSD 上,存储设备不会受到“邻居噪音”问题的影响。

集合(set)是记录的逻辑分组,类似于 RDBMS 中的表。表具有严格定义的模式来规定表中所有行的结构,而 Aerospike 的集合是无模式的,因此可以存储结构化或非结构化数据。

记录(record)类似于关系数据库中的行。它包含多个数据项,存储在 bin 中(类似于关系数据库中的列)。由于 Aerospike 是无模式的,每个记录是自描述的,可以被视为类似于映射数据类型:bin 名称作为映射的键,值则是 bin 的内容。

bin 中的值可以是标量(例如字符串、整数或浮点数),也可以是复杂的列表或映射。这些列表和映射(统称为集合数据类型,简称 CDT)可以互相嵌套,使单个 bin 中可以包含任意复杂的数据结构。表 2-1 总结了 Aerospike 和 RDBMS 的术语对照。

Aerospike 术语RDBMS 术语
NamespaceDatabase/Tablespace
SetTable
RecordRecord/Row
BinField/Column

请注意,在图 2-4 中,记录 2 没有“updated_at”字段,这与图 2-3 中的 RDBMS 示例类似。然而,与 RDBMS 不同的是,Aerospike 中该值不是 null,而是在记录中不存在。由于每个记录是自描述的,因此不同记录可以包含不同数量或类型的 bin。同样,不同记录中具有相同名称的 bin 可以是不同的类型。例如,一个记录可能有一个名为“data”的 bin,包含整数,而另一个记录可能有一个同名的 bin,但包含字符串。虽然这种情况较少见,但通常生成数据的应用会在同一集合中的记录中存储相同类型的数据,从而在集合上有效地实施了模式。

另外,每个记录中的 bin 都有已知类型。该类型在将数据写入 bin 时确定,改变 bin 的内容会改变其类型。以下是一个 Java 示例代码:

client.put(null, key, new Bin("data", 10));    // long
client.put(null, key, new Bin("data", 10.0));  // double
client.put(null, key, new Bin("data", "10"));  // String

在此示例中,相同的 bin 被设置了三个不同的值,尽管它们的表示方式相似。在第一个示例中,传递给 bin 的值是整数 10,因此会作为整数存储。在第二个示例中,传递的值是双精度浮点数,因此 Aerospike 会将其存储为 double 类型,而在最后一个示例中,值将作为字符串存储。需要注意的是,Aerospike 没有明显的 API 调用来确定 bin 的类型。可以读取 bin 并检查其内容,或者使用表达式来确定 bin 类型,这将在第 4 章中介绍。

注意

Aerospike 内部始终将整数值存储为 64 位整数,浮点数存储为 64 位双精度值。因此,将 10 存储为整数、长整型或短整型在 Aerospike 中没有区别,它们都将被相同地对待。

bin 的类型会影响对该 bin 可以执行的操作。例如,如果 bin 中存储的是字符串,则可以执行字符串追加操作,但无法向其添加整数。如果 bin 值被新的不同类型的值替换,新的 bin 类型将决定接下来允许的操作。尽管可以通过表达式将 bin 转换为不同类型,但通常不需要,因为应用会在 bin 上强制执行正确的类型模式。

简单的第一个应用程序

现在你已经了解了数据模型,让我们来创建一个简单的 Java 程序。在这个程序中,你将连接到数据库,在 test 命名空间的 Item 集合中插入一条记录,然后读取该记录并打印出记录中的信息:

public class FirstProgram {
    public static void main(String[] args) {
        IAerospikeClient client = new AerospikeClient(null, "127.0.0.1", 3000);
        Key key = new Key("test", "item", 1);
        client.put(null, key, new Bin("name", "Stylish Couch"), 
               new Bin("cost", 50000), new Bin("discount", 0.21));
        Record item = client.get(null, key);
        System.out.printf("%s costs $%.02f with a %d%% discount\n",
               item.getString("name"), item.getInt("cost")/100.0,
              (int)(100*item.getFloat("discount")));
        client.close();
    }
}

在 Python 中:

import aerospike

client = aerospike.client({'hosts':[('127.0.0.1', 3000)]}).connect()
key = ('test', 'item', 1)
bins = {
    'name': 'Stylish Couch',
    'cost': 50000,
    'discount': 0.21
}
client.put(key, bins)
(key_, meta, bins) = client.get(key)
print('Record:', bins)
client.close()

此程序展示了 Aerospike 客户端驱动在 Java 和 Python 中的用法。在 Java 中,需要导入该驱动库,具体导入方式取决于使用的构建工具。例如,使用 Apache Maven 时,可以添加如下依赖项:

<dependency>
    <groupId>com.aerospike</groupId>
    <artifactId>aerospike-client</artifactId>
    <version>6.1.10</version>
</dependency>

在 Gradle 中,可以这样写:

implementation group: 'com.aerospike', name: 'aerospike-client', version: '6.1.10'

对于其他语言或构建工具的具体用法,或直接下载客户端 JAR 文件,可以访问 Aerospike 客户端下载页面并找到对应的语言部分。请注意,服务器版本和客户端版本不需要一致,通常最好使用最新版本。客户端和服务器会尽量保持向后兼容,因此可以在更新服务器的同时保持客户端版本不变,反之亦然。

现在,让我们来分析第一个程序。

建立数据库连接

第一步是建立与数据库的连接:

ClientPolicy clientPolicy = new ClientPolicy();
clientPolicy.user = "admin";
clientPolicy.password = "adminPassword";
IAerospikeClient client = new AerospikeClient(null, "127.0.0.1", 3000);

在 Python 中:

client = aerospike.client({'hosts':[('127.0.0.1', 3000)]}).connect('admin', 'adminPassword')

建立与数据库的连接只需三个参数:

  1. ClientPolicy:定义如何连接数据库。Aerospike 中几乎所有操作的第一个参数都是某种策略,这里传递用户名和密码用于登录启用了安全功能的集群(企业版功能)。像所有 Aerospike 策略一样,也可以传递 null 以使用默认设置。
  2. Aerospike 服务器的主机地址:可以是主机名、DNS 地址或 IP 地址。
  3. Aerospike 监听的主机端口:默认是 3000 端口。

请注意,这段代码在生产系统中并不理想。Aerospike 是一个集群数据库,通常有多个完全相同的机器来存储数据,这样可以避免集群中的单点故障。然而,我们的代码仅连接到单个节点,如果该节点宕机,应用将无法连接。更健壮的构造函数应传递多个地址,类似如下:

IAerospikeClient client = new AerospikeClient(clientPolicy,
        new Host("172.17.0.2", 3000), 
        new Host("172.17.0.3", 3000), 
        new Host("172.17.0.4", 3000));

在 Python 中:

config = {
    'hosts': [
        ('172.17.0.2', 3000),
        ('172.17.0.3', 3000),
        ('172.17.0.4', 3000)
    ]
}

这些节点称为种子节点。创建 AerospikeClient 时,Aerospike 从列表中的第一个种子节点开始尝试连接,如果无法连接,则继续尝试下一个种子节点,以此类推。如果所有种子节点均无法连接,则会抛出异常。

如果其中一个种子节点连接到集群,Aerospike 将使用该集群作为数据库,其他种子节点将不再尝试。种子节点的响应包含集群中所有节点的信息,客户端接收该信息后尝试与每个节点建立连接池。

Aerospike 集群是动态的。节点可以随时添加或移除,移除可能是计划的(如停机维护操作系统)或非计划的(如硬件故障)。因此,客户端需要不断刷新对集群节点的了解,这个过程称为集群管理,默认每秒执行一次。如果客户端对集群的视图不准确,并将请求发送到错误的节点,集群足够智能,可以将请求代理到正确的数据节点。

在应用结束时,应该调用 close() 方法来关闭客户端,以释放客户端和服务器上的资源:

client.close();

请注意,一旦客户端关闭,就不能再次使用它。例如,如果你有一个持续运行的在线程序创建了 AerospikeClient,并且偶尔运行一个批处理作业重用相同的客户端,则批处理程序在完成时不应调用 close(),否则会影响在线应用。

创建后,客户端设计为多线程使用。通常一个程序只需要一个客户端,所有对数据库的请求都应使用相同的客户端。正如所见,创建新的 AerospikeClient 是一个代价高昂的操作——可能需要尝试多个种子节点、创建连接池并初始化管理集群的线程。因此,频繁创建和关闭客户端会显著降低应用性能。

插入和检索数据

要通过主键(PK)在 Aerospike 中插入或检索数据,需要创建一个 Key。Key 包含三个组成部分:

  • 命名空间
  • 集合名称
  • 主键值

在本例中,可以按如下方式创建一个 Key:

Key key = new Key("test", "item", 1);

在 Python 中:

key = ('test', 'item', 1)

这里命名空间为 test,你将使用 item 集合中主键为 1 的记录。

要在 item 集合中插入一条数据记录,Aerospike 需要传入策略(policy)、键(key)和要插入的数据:

client.put(null, key, new Bin("name", "Stylish Couch"), 
               new Bin("cost", 50000), new Bin("discount", 0.21));

在 Python 中:

bins = {
    'name': 'Stylish Couch',
    'cost': 50000,
    'discount': 0.21
}
client.put(key, bins)

此操作将在数据库中创建一条新记录,包含三列——name(字符串)、cost(整数)和 discount(浮点数)。

注意

在 Aerospike 中,所有修改记录的操作默认是 UPSERT(插入或更新)——如果记录已存在,则更新它;如果记录不存在,则创建新记录。第 3 章将详细介绍这一点。同样,如果集合不存在,Aerospike 在插入数据时会自动创建该集合。

Aerospike 也区分大小写,例如集合 testSet 与集合 TestSet 是不同的。然而,当使用如 Aerospike Quick Look 等工具时,诸如 SELECT 之类的关键字不区分大小写。

要检索记录,需要提供策略和要检索的键:

Record item = client.get(null, key);

在 Python 中:

(key_, meta, bins) = client.get(key);

注意,此处使用的 Record 类是 com.aerospike.client.Record。Java 16 引入了 java.lang.Record,一些 IDE 会默认尝试使用它,因此需要在类的导入语句中显式列出 com.aerospike.client.Record

返回的 Record 包含记录中的所有值,可以通过调用 get 方法来检索这些值。如果已知要检索的数据类型,可以使用特定方法,如 getString()getInt() 等。如果返回值的类型未知,可以使用 getValue() 方法。

使用 Aerospike Quick Look

恭喜你!你已经编写了第一个使用 Aerospike 的程序。你插入了一条记录并检索它,以验证记录是否正确插入。无需编写代码也可以查看和更改 Aerospike 数据库中的记录,方法之一是使用 Aerospike Quick Look (AQL)。AQL 使用类似 SQL 的方言来操作数据,但需要注意,这并不是一个 SQL 工具,虽然有些命令看起来像 SQL,但不支持通用 SQL 查询。

在终端中,可以通过执行 % aql 启动 AQL 的交互模式。

注意

aql 默认尝试连接到本地主机(127.0.0.1)。可以使用 -h 选项连接到其他 IP 地址。例如,你的 Docker 容器中运行的 Aerospike 可能在 172.17.0.2 上,可以通过 % aql -h 172.17.0.2 连接到该地址。

如果你在 AeroLab 中运行集群,可以通过在主机名后加上端口号来更改端口,例如:% aql -h localhost:3100

这将显示一些基本信息:

% aql
Seed: 127.0.0.1
User: None
Config File: /etc/aerospike/astools.conf / Users/book/.aerospike/astools.conf
Aerospike Query Client
Version 8.1.0
C Client Version 6.3.0
Copyright 2012-2024 Aerospike. All rights reserved.
aql>

在此可以使用 AQL 查询数据:

aql> select * from test.item where pk =1

这将显示该特定主键的数据信息:

+----+------------------+-------+----------+
| PK | name             | cost  | discount |
+----+------------------+-------+----------+
| 1  | "Stylish Couch"  | 50000 | 0.21     |
+----+------------------+-------+----------+
1 row in set (0.000 secs)
OK

你也可以插入一条新记录或更新现有记录,插入和更新的语义相同,因为在 Aerospike 中没有 INSERT 和 UPDATE 的区别。要更新记录,请使用:

aql> insert into test.item(Pk, cost) values (1, 60000)
OK, 1 record affected.
aql> select * from test.item where pk =1
+----+------------------+-------+----------+
| PK | name             | cost  | discount |
+----+------------------+-------+----------+
| 1  | "Stylish Couch"  | 60000 |  0.21    |
+----+------------------+-------+----------+
1 row in set (0.001 secs)
OK

该命令将更新 bin(cost)并与现有记录合并,未更新的列保持不变。

插入新记录时需要提供所有 bin:

aql> insert into test.item(PK, name, cost, discount) 
values (2, "Blue Chair", 7399, 0.03)

可以查看所有记录:

aql> select * from test.item
+----+------------------+-------+----------+
| PK | name             | cost  | discount |
+----+------------------+-------+----------+
|  2 | "Blue Chair"     | 7399  |  0.03    |
|  1 | "Stylish Couch"  | 60000 |  0.21    |
+----+------------------+-------+----------+
2 rows in set (0.164 secs)

AQL 对基本数据操作非常有用,但距离完整的 SQL 实现还有很大差距。表 2-2 列出了一些最常用的 AQL 命令。

命令用法
help显示 AQL 支持的命令。不同版本的 AQL 可能支持不同的命令,使用 help 总会显示支持的命令。
INSERT INTO <ns>[.<set>] (PK, <bins>) VALUES (<key>, <values>)在指定记录中插入或更新 bin。由于所有 INSERT 实际上是 UPSERT,因此没有特定的 UPDATE 命令。使用 PK 指代主键。例如:insert into test.testSet(PK, name, age) values (1, 'Tim', 312)
DELETE FROM <ns>[.<set>] WHERE PK = <key>删除单条记录。例如:delete from test.testSet where PK = 1
SELECT <bins> FROM <ns>[.<set>]检索集合中的所有记录并显示它们。对于大型集合,通常会超时,因为默认超时时间为 1 秒。例如:select * from test.testSet
SELECT <bins> FROM <ns>[.<set>] WHERE PK = <key>从 Aerospike 数据库中检索单条记录。例如:select name, age from test.testSet where PK = 1
SELECT <bins> FROM <ns>[.<set>] WHERE <bin> = <value>选择符合特定条件的记录。注意,where 子句中的 bin 必须定义有二级索引(二级索引将在第 4 章讨论)。例如:select * from test.testSet where name = 'Tim'
SHOW NAMESPACES显示数据库中所有命名空间的详细信息。
SHOW SETS显示数据库中所有集合的详细信息。
`SET OUTPUT JSONRAW

正如前面提到的,Aerospike 区分大小写:table1Table1 不同,TABLE1 也不同。然而,AQL 关键字(如 SELECTDELETEFROM 等)不区分大小写。此外,AQL 设计为通过一些命令提供类似 SQL 的方言,但不适合批量数据插入,也不支持比表 2-2 或 aql help 命令输出中显示的更复杂的操作。

策略(Policies)

目前为止你所看到的几乎所有调用都需要一个策略。策略决定特定调用的行为,例如在网络出现问题或记录已存在时如何处理。到目前为止,我们很方便地将这些策略设置为 null,这样使用了默认值,但我们来看一下策略为何重要。这里我们将重点放在 put 命令上:

client.put(null, key, new Bin("name", "Stylish Couch"), 
               new Bin("cost", 50000), new Bin("discount", 0.21));

在上述语句中,第一个参数为 null,实际上是一个 WritePolicy,它是 Policy 类的子类。Policy 类控制一些对大多数调用有用的通用参数,例如网络信息。表 2-3 列出了一些该类中常用的字段。

字段默认值描述
socketTimeout30000在将事务发送到服务器时等待的毫秒数,超时后将触发重试,具体取决于后续的设置。
maxRetries2在发送到服务器时发生超时时,重试的最大次数。读取的默认重试次数为 2,写入为 0,扫描/查询为 5。
sleepBetweenRetries0如果可以重试事务,在重试之间等待的毫秒数。
totalTimeout1000在放弃之前的事务和所有重试的总等待时间(毫秒)。如果超出该时间,将抛出 Timeout 异常,即使尚未超过重试次数。
compressfalse是否压缩客户端与服务器之间的数据。对于大型操作,这可能减少网络流量,但增加 CPU 占用。
filterExpnull是否基于过滤表达式在服务器上过滤操作。表达式将在本书后续章节中详细讨论。

这些网络设置在生产应用中尤为重要。它们规定了在服务器未及时响应时客户端层的反应。例如,如果服务器因硬件故障宕机,有一段时间客户端和集群的其余部分无法知道服务器已宕机,仍会向其发送请求。这会导致客户端超时,但这些设置可以允许客户端在需要时重试操作。

WritePolicy 子类本身也具有一些对应用有用的属性。表 2-4 列出了一些主要的字段。

字段默认值描述
durableDeletefalse当显式删除记录时,是否应在记录中放置墓碑标记,还是快速清除?在某些极端情况下,清除的记录可能会在数据库中重新出现。持久删除是企业版独有的功能,推荐对数据完整性至关重要的数据使用该功能。
expiration0Aerospike 可自动移除记录,该字段指定记录的保留时间(秒)。此时间过后,记录将自动被 Aerospike 移除。如果该字段为 -1,则记录将永久存在,除非显式删除。如果该字段为 0,则使用命名空间指定的默认生存时间。
generationgenerationPolicy0这两个字段一起使用,允许应用使用乐观并发实现线程安全的读-改-写周期。我们将在后续章节详细讨论。
recordExistsActionUPDATE当操作更改记录时,如果已存在记录,应如何将更改与现有记录合并。

为说明这些策略的用法,请看我们简单程序中的写入操作:

client.put(null, key, new Bin("name", "Stylish Couch"), 
               new Bin("cost", 50000), new Bin("discount", 0.21));

由于策略为 null,因此将使用默认值,默认的 recordExistsActionUPDATE。这将更新已存在的记录,若记录不存在则创建新记录。假设业务要求仅在调用时创建记录,若记录已存在则应抛出异常,这对应于 recordExistsActionCREATE_ONLY。为实现这一点,可将代码更改如下:

WritePolicy writePolicy = new WritePolicy(client.getWritePolicyDefault());
writePolicy.recordExistsAction = RecordExistsAction.CREATE_ONLY;
client.put(writePolicy, key, new Bin("name", "Stylish Couch"), 
               new Bin("cost", 50000), new Bin("discount", 0.21));

在 Python 中:

writePolicy = {'exists': aerospike.POLICY_EXISTS_CREATE}
client.put(key, bins, policy=writePolicy)

在这种情况下,需要创建一个新的 WritePolicy。如果仅通过以下方式实例化 WritePolicy

WritePolicy writePolicy = new WritePolicy();

这样也可以,但参数值将是硬编码在 Aerospike 客户端驱动中的默认值。应用通常会使用一组符合其 SLA 环境的超时设置作为所有调用的起点,调用时可根据具体要求覆盖这些设置。

为实现这一点,ClientPolicy 具有一组默认参数,每种策略类型各一个。基于 writePolicyDefault 创建新 WritePolicy 将生成一个单独的策略实例,适用于此特定调用,而不影响其他线程中的并发写入调用,同时保留大部分默认设置,仅改变一处。

代码随后将 recordExistsAction 设置为 CREATE_ONLY。Aerospike 服务器会检查是否已存在具有该键的记录,如果存在将抛出异常;如果不存在,则新记录将插入数据库。注意,此操作是原子性的——在服务器检查记录是否存在与插入记录之间,无法由其他线程创建该记录。

总结

在本章中,你安装了 Aerospike 并编写了第一个程序。你学习了如何读取和写入记录,并使用 AQL 工具检查和更改数据。最后,你了解了策略以及它们如何影响操作。在下一章中,你将从客户端的角度更深入地探索 Aerospike 的功能。