多线程 Aerospike 中的慢查询聚合
Slow queryAggreate in Aerospike with multithreading
我们正在创建一个 Java 客户端将数据直接写入 Aerospike 的内存,另一个 Java 客户端从内存中读取数据。
两个客户端都是多线程的。
在我们的读取客户端中有几个queryAggregate操作,是用UDF实现的。
我们面临如下问题:
如果我们只为写入操作分配 1 个线程,为读取操作分配 2 个线程,那么我们有大约 25K TPS 用于读取。
如果我们为写操作分配 2 个线程,为读操作保持相同数量的线程,那么我们只有不到 10K TPS 用于读取。
Aerospike 服务器 运行 在一台有 24 个物理 CPU 核心的机器中。写入和读取客户端同时在这台机器上 运行。
服务器几乎是 运行 Aerospike 服务器而已。 CPU 资源完全免费。
以下是我们当前的 Aerospike 服务器配置:
paxos-single-replica-limit=1;pidfile=null;proto-fd-max=15000;advertise-ipv6=false;auto-pin=none;batch-threads=4;batch-max-buffers-per-queue=255;batch-max-requests=5000;batch-max-unused-buffers=256;batch-priority=200;batch-index-threads=24;clock-skew-max-ms=1000;cluster-name=null;enable-benchmarks-fabric=false;enable-benchmarks-svc=false;enable-hist-info=false;hist-track-back=300;hist-track-slice=10;hist-track-thresholds=null;info-threads=16;log-local-time=false;migrate-max-num-incoming=4;migrate-threads=1;min-cluster-size=1;node-id-interface=null;nsup-delete-sleep=100;nsup-period=120;nsup-startup-evict=true;proto-fd-idle-ms=60000;proto-slow-netio-sleep-ms=1;query-batch-size=100;query-buf-size=2097152;query-bufpool-size=256;query-in-transaction-thread=false;query-long-q-max-size=500;query-microbenchmark=false;query-pre-reserve-partitions=false;query-priority=10;query-priority-sleep-us=1;query-rec-count-bound=18446744073709551615;query-req-in-query-thread=false;query-req-max-inflight=100;query-short-q-max-size=500;query-threads=6;query-threshold=10;query-untracked-time-ms=1000;query-worker-threads=15;run-as-daemon=true;scan-max-active=100;scan-max-done=100;scan-max-udf-transactions=32;scan-threads=4;service-threads=24;sindex-builder-threads=4;sindex-gc-max-rate=50000;sindex-gc-period=10;ticker-interval=10;transaction-max-ms=1000;transaction-pending-limit=20;transaction-queues=4;transaction-retry-ms=1002;transaction-threads-per-queue=4;work-directory=/opt/aerospike;debug-allocations=none;fabric-dump-msgs=false;max-msgs-per-type=-1;prole-extra-ttl=0;service.port=3000;service.address=any;service.access-port=0;service.alternate-access-port=0;service.tls-port=0;service.tls-access-port=0;service.tls-alternate-access-port=0;service.tls-name=null;heartbeat.mode=multicast;heartbeat.multicast-group=239.1.99.222;heartbeat.port=9918;heartbeat.interval=150;heartbeat.timeout=10;heartbeat.mtu=1500;heartbeat.protocol=v3;fabric.port=3001;fabric.tls-port=0;fabric.tls-name=null;fabric.channel-bulk-fds=2;fabric.channel-bulk-recv-threads=4;fabric.channel-ctrl-fds=1;fabric.channel-ctrl-recv-threads=4;fabric.channel-meta-fds=1;fabric.channel-meta-recv-threads=4;fabric.channel-rw-fds=8;fabric.channel-rw-recv-threads=16;fabric.keepalive-enabled=true;fabric.keepalive-intvl=1;fabric.keepalive-probes=10;fabric.keepalive-time=1;fabric.latency-max-ms=5;fabric.recv-rearm-threshold=1024;fabric.send-threads=8;info.port=3003;enable-security=false;privilege-refresh-period=300;report-authentication-sinks=0;report-data-op-sinks=0;report-sys-admin-sinks=0;report-user-admin-sinks=0;report-violation-sinks=0;syslog-local=-1
下面是 aerospike.conf 文件:
# Aerospike database configuration file for use with systemd.
service {
paxos-single-replica-limit 1 # Number of nodes where the replica count is automatically reduced to 1.
proto-fd-max 15000
}
logging {
console {
context any info
}
}
network {
service {
address any
port 3000
}
heartbeat {
mode multicast
multicast-group 239.1.99.222
port 9918
# To use unicast-mesh heartbeats, remove the 3 lines above, and see
# aerospike_mesh.conf for alternative.
interval 150
timeout 10
}
fabric {
port 3001
}
info {
port 3003
}
}
namespace test {
replication-factor 2
memory-size 4G
default-ttl 30d # 30 days, use 0 to never expire/evict.
storage-engine memory
}
namespace bar {
replication-factor 2
memory-size 4G
default-ttl 30d # 30 days, use 0 to never expire/evict.
storage-engine memory
# To use file storage backing, comment out the line above and use the
# following lines instead.
# storage-engine device {
# file /opt/aerospike/data/bar.dat
# filesize 16G
# data-in-memory true # Store data in memory in addition to file.
# }
}
有人可以告诉我们目前的瓶颈在哪里吗?
如何在增加写入线程数的同时提高读取速度?
上面的配置是默认的,我们还没有做任何更改。
我不确定你在说什么:
首先,我不知道您所说的使用 1 个线程或 2 个线程阅读是什么意思。您是说您使用了 AerospikeClient 的 2 个实例。这些是分布在不同的客户端机器上,还是它们都在同一个实例上?
下一点,Java 客户端是多线程的(不是你写的 1 个线程或 2 个线程)。如果您使用的是同步客户端,则每个操作都将 运行 在一个线程中等待响应。请查看 Aerospike 网站上的 introduction to the Java client。
您的 Aerospike 集群只是一个节点吗?它不能只用一个节点做复制因子 2。
谓词过滤与基于 UDF 的逻辑
无论您在流 UDF 的过滤器中执行什么逻辑,请尝试将其移至 predicate filtering instead. In the Java client this is implemented in the PredExp class (see the examples。
配置调整
您正在进行写入和查询,没有单条记录读取或批量读取。您应该调低批处理索引线程,并调高查询线程。
您有两个配置相同的内存中命名空间。杀死 foo
和 bar
让我们定义一个不同的:
service {
paxos-single-replica-limit 1 # Number of nodes where the replica count is automatically reduced to 1.
proto-fd-max 15000
batch-index-threads 2 # you don't need 24 batch threads, you're not using them
query-threads 24 # setting it to #cpu
query-in-transaction-thread true # because you query an in-memory namespace
query-priority 40
# auto-pin cpu # uncomment this if you have kernel >= 3.19
}
logging {
console {
context any info
}
}
network {
service {
address any
port 3000
}
heartbeat {
mode multicast
multicast-group 239.1.99.222
port 9918
# To use unicast-mesh heartbeats, remove the 3 lines above, and see
# aerospike_mesh.conf for alternative.
interval 150
timeout 10
}
fabric {
port 3001
}
info {
port 3003
}
}
namespace demo {
replication-factor 2
memory-size 10G
partition-tree-sprigs 4096 # maximize these for in-memory, you have plenty of DRAM
default-ttl 30d
storage-engine memory
}
我相信你应该
- 减少批处理索引线程数 (
batch-index-threads
)
- 将
query-threads
的数量增加到每个 CPU 核心一个
- 提高
query-priority
- 因为您正在使用内存中的命名空间,所以我将
query-in-transaction-thread
配置参数设置为 true
。
- 最大化
partition-tree-sprigs
. I suggest that you should be using auto-pin cpu
。
参见:What's New In Aerospike 3.12, What's New in Aerospike 3.13 & 3.14
还有什么?
根据您使用调整后的配置获得的结果,这还有待观察。稍后您需要计算出系统中有多少个对象以及它们的平均对象大小是多少以进行容量规划。
通过 1 个线程或 2 个线程读取,我的意思是我们只使用 1 个同步的 Java 客户端连接到服务器,但客户端有多个线程从内存中并行读取。服务器只是 1 个节点。我们已将所有逻辑块从流 UDF 移动到 Java 代码,现在速度要好得多。我们还按照您的建议更改了 batch-index-threads、query-threads、query-in-transaction-thread、query-priority,但速度似乎并没有提高。然而,重要的是使用纯 Java 代码进行查询比流 UDF 快得多。非常感谢您的详细回复 Ronen。肯定对我们有很大帮助。
我们正在创建一个 Java 客户端将数据直接写入 Aerospike 的内存,另一个 Java 客户端从内存中读取数据。 两个客户端都是多线程的。
在我们的读取客户端中有几个queryAggregate操作,是用UDF实现的。
我们面临如下问题:
如果我们只为写入操作分配 1 个线程,为读取操作分配 2 个线程,那么我们有大约 25K TPS 用于读取。
如果我们为写操作分配 2 个线程,为读操作保持相同数量的线程,那么我们只有不到 10K TPS 用于读取。
Aerospike 服务器 运行 在一台有 24 个物理 CPU 核心的机器中。写入和读取客户端同时在这台机器上 运行。 服务器几乎是 运行 Aerospike 服务器而已。 CPU 资源完全免费。
以下是我们当前的 Aerospike 服务器配置:
paxos-single-replica-limit=1;pidfile=null;proto-fd-max=15000;advertise-ipv6=false;auto-pin=none;batch-threads=4;batch-max-buffers-per-queue=255;batch-max-requests=5000;batch-max-unused-buffers=256;batch-priority=200;batch-index-threads=24;clock-skew-max-ms=1000;cluster-name=null;enable-benchmarks-fabric=false;enable-benchmarks-svc=false;enable-hist-info=false;hist-track-back=300;hist-track-slice=10;hist-track-thresholds=null;info-threads=16;log-local-time=false;migrate-max-num-incoming=4;migrate-threads=1;min-cluster-size=1;node-id-interface=null;nsup-delete-sleep=100;nsup-period=120;nsup-startup-evict=true;proto-fd-idle-ms=60000;proto-slow-netio-sleep-ms=1;query-batch-size=100;query-buf-size=2097152;query-bufpool-size=256;query-in-transaction-thread=false;query-long-q-max-size=500;query-microbenchmark=false;query-pre-reserve-partitions=false;query-priority=10;query-priority-sleep-us=1;query-rec-count-bound=18446744073709551615;query-req-in-query-thread=false;query-req-max-inflight=100;query-short-q-max-size=500;query-threads=6;query-threshold=10;query-untracked-time-ms=1000;query-worker-threads=15;run-as-daemon=true;scan-max-active=100;scan-max-done=100;scan-max-udf-transactions=32;scan-threads=4;service-threads=24;sindex-builder-threads=4;sindex-gc-max-rate=50000;sindex-gc-period=10;ticker-interval=10;transaction-max-ms=1000;transaction-pending-limit=20;transaction-queues=4;transaction-retry-ms=1002;transaction-threads-per-queue=4;work-directory=/opt/aerospike;debug-allocations=none;fabric-dump-msgs=false;max-msgs-per-type=-1;prole-extra-ttl=0;service.port=3000;service.address=any;service.access-port=0;service.alternate-access-port=0;service.tls-port=0;service.tls-access-port=0;service.tls-alternate-access-port=0;service.tls-name=null;heartbeat.mode=multicast;heartbeat.multicast-group=239.1.99.222;heartbeat.port=9918;heartbeat.interval=150;heartbeat.timeout=10;heartbeat.mtu=1500;heartbeat.protocol=v3;fabric.port=3001;fabric.tls-port=0;fabric.tls-name=null;fabric.channel-bulk-fds=2;fabric.channel-bulk-recv-threads=4;fabric.channel-ctrl-fds=1;fabric.channel-ctrl-recv-threads=4;fabric.channel-meta-fds=1;fabric.channel-meta-recv-threads=4;fabric.channel-rw-fds=8;fabric.channel-rw-recv-threads=16;fabric.keepalive-enabled=true;fabric.keepalive-intvl=1;fabric.keepalive-probes=10;fabric.keepalive-time=1;fabric.latency-max-ms=5;fabric.recv-rearm-threshold=1024;fabric.send-threads=8;info.port=3003;enable-security=false;privilege-refresh-period=300;report-authentication-sinks=0;report-data-op-sinks=0;report-sys-admin-sinks=0;report-user-admin-sinks=0;report-violation-sinks=0;syslog-local=-1
下面是 aerospike.conf 文件:
# Aerospike database configuration file for use with systemd.
service {
paxos-single-replica-limit 1 # Number of nodes where the replica count is automatically reduced to 1.
proto-fd-max 15000
}
logging {
console {
context any info
}
}
network {
service {
address any
port 3000
}
heartbeat {
mode multicast
multicast-group 239.1.99.222
port 9918
# To use unicast-mesh heartbeats, remove the 3 lines above, and see
# aerospike_mesh.conf for alternative.
interval 150
timeout 10
}
fabric {
port 3001
}
info {
port 3003
}
}
namespace test {
replication-factor 2
memory-size 4G
default-ttl 30d # 30 days, use 0 to never expire/evict.
storage-engine memory
}
namespace bar {
replication-factor 2
memory-size 4G
default-ttl 30d # 30 days, use 0 to never expire/evict.
storage-engine memory
# To use file storage backing, comment out the line above and use the
# following lines instead.
# storage-engine device {
# file /opt/aerospike/data/bar.dat
# filesize 16G
# data-in-memory true # Store data in memory in addition to file.
# }
}
有人可以告诉我们目前的瓶颈在哪里吗? 如何在增加写入线程数的同时提高读取速度?
上面的配置是默认的,我们还没有做任何更改。
我不确定你在说什么:
首先,我不知道您所说的使用 1 个线程或 2 个线程阅读是什么意思。您是说您使用了 AerospikeClient 的 2 个实例。这些是分布在不同的客户端机器上,还是它们都在同一个实例上?
下一点,Java 客户端是多线程的(不是你写的 1 个线程或 2 个线程)。如果您使用的是同步客户端,则每个操作都将 运行 在一个线程中等待响应。请查看 Aerospike 网站上的 introduction to the Java client。
您的 Aerospike 集群只是一个节点吗?它不能只用一个节点做复制因子 2。
谓词过滤与基于 UDF 的逻辑
无论您在流 UDF 的过滤器中执行什么逻辑,请尝试将其移至 predicate filtering instead. In the Java client this is implemented in the PredExp class (see the examples。
配置调整
您正在进行写入和查询,没有单条记录读取或批量读取。您应该调低批处理索引线程,并调高查询线程。
您有两个配置相同的内存中命名空间。杀死 foo
和 bar
让我们定义一个不同的:
service {
paxos-single-replica-limit 1 # Number of nodes where the replica count is automatically reduced to 1.
proto-fd-max 15000
batch-index-threads 2 # you don't need 24 batch threads, you're not using them
query-threads 24 # setting it to #cpu
query-in-transaction-thread true # because you query an in-memory namespace
query-priority 40
# auto-pin cpu # uncomment this if you have kernel >= 3.19
}
logging {
console {
context any info
}
}
network {
service {
address any
port 3000
}
heartbeat {
mode multicast
multicast-group 239.1.99.222
port 9918
# To use unicast-mesh heartbeats, remove the 3 lines above, and see
# aerospike_mesh.conf for alternative.
interval 150
timeout 10
}
fabric {
port 3001
}
info {
port 3003
}
}
namespace demo {
replication-factor 2
memory-size 10G
partition-tree-sprigs 4096 # maximize these for in-memory, you have plenty of DRAM
default-ttl 30d
storage-engine memory
}
我相信你应该
- 减少批处理索引线程数 (
batch-index-threads
) - 将
query-threads
的数量增加到每个 CPU 核心一个 - 提高
query-priority
- 因为您正在使用内存中的命名空间,所以我将
query-in-transaction-thread
配置参数设置为true
。 - 最大化
partition-tree-sprigs
. I suggest that you should be usingauto-pin cpu
。
参见:What's New In Aerospike 3.12, What's New in Aerospike 3.13 & 3.14
还有什么?
根据您使用调整后的配置获得的结果,这还有待观察。稍后您需要计算出系统中有多少个对象以及它们的平均对象大小是多少以进行容量规划。
通过 1 个线程或 2 个线程读取,我的意思是我们只使用 1 个同步的 Java 客户端连接到服务器,但客户端有多个线程从内存中并行读取。服务器只是 1 个节点。我们已将所有逻辑块从流 UDF 移动到 Java 代码,现在速度要好得多。我们还按照您的建议更改了 batch-index-threads、query-threads、query-in-transaction-thread、query-priority,但速度似乎并没有提高。然而,重要的是使用纯 Java 代码进行查询比流 UDF 快得多。非常感谢您的详细回复 Ronen。肯定对我们有很大帮助。