All Products
Search
Document Center

PolarDB:Performance testing report

Last Updated:Mar 28, 2026

This report presents YCSB (Yahoo! Cloud Serving Benchmark) benchmark results for PolarDB for PostgreSQL's DynamoDB-compatible API across five workload types and four data scales. Use these results for technology selection, application design, and capacity planning.

Point query performance peaks at 109,808 OPS (Operations Per Second) at 100 GB data volume.

Test result summary

The following table shows peak OPS for each workload at different data volumes. Each value is the highest OPS observed across all concurrency levels tested.

Test scenario1 GB10 GB100 GB1 TB
100% write (Insert)41,43039,86133,35736,248
100% update (Update)44,17741,48638,06230,782
100% read (Read)80,57382,856109,80875,108
50% read + 50% update45,01042,96239,80532,021
100% range scan (Scan)1,0731,0891,075922
All values are peak OPS obtained by running each workload at multiple concurrency levels and recording the highest result.

Test environment

Environment configuration

ComponentSpecifications
Test cluster (PolarDB)1 cluster; PostgreSQL 14 kernel; Enterprise Edition; Dedicated, 16-core 64 GB
Stress testing client (ECS)1 client; 16-core 64 GB; Alibaba Cloud Linux 3.2104 LTS 64-bit
Deployment regionBeijing, Zone K

Benchmark tool

Workload model

All workloads use YCSB CoreWorkload. The test data model uses records that each contain 10 fields, with each field value at 100 bytes, making each record approximately 1 KB.

YCSB workloadScenarioCore parameter
workload_insert_only100% writeinsertproportion=1.0
workload_update_only100% updateupdateproportion=1.0
workload_read_only100% point queryreadproportion=1.0
workload_read_update50% read + 50% updatereadproportion=0.5, updateproportion=0.5
workload_scan_only100% range scanscanproportion=1.0

Appendix: Reproduce the tests

Follow these steps to reproduce the performance tests for secondary validation or custom testing.

Prerequisites

Before you begin, make sure you have:

Step 1: Configure YCSB

Configure identity credentials

Edit dynamodb/conf/AWSCredentials.properties and enter the credentials for your dedicated DynamoDB account:

# The account name is the AccessKey ID
accessKey = <YOUR_ACCESS_KEY_ID>

# Secret key
secretKey = <YOUR_SECRET_ACCESS_KEY>

Configure connection properties

Edit dynamodb/conf/dynamodb.properties and specify the PolarDB cluster connection:

# The absolute path of the authentication file
dynamodb.awsCredentialsFile = /path/to/your/AWSCredentials.properties

# Create a usertable table in the cluster in advance.
# This example creates a usertable table with only a partition key named pk.
dynamodb.primaryKey = pk

# The DynamoDB endpoint for PolarDB. Must include the http:// prefix.
dynamodb.endpoint = http://<your-polardb-ddb-endpoint>:<port>

# Leave the region parameter blank.
dynamodb.region =

# The primary key name and type for the test table. Must match the test table.
dynamodb.primaryKey = HASH

Step 2: Patch the YCSB update operation

Important

The official YCSB DynamoDB client uses the deprecated AttributeUpdates parameter by default. PolarDB requires the UpdateExpression parameter. You must apply the following change before running any update workloads.

File to modify: dynamodb/src/main/java/site/ycsb/db/DynamoDBClient.java Method to modify: update()

Replace the original implementation:

@Override
public Status update(String table, String key, Map<String, ByteIterator> values) {
  if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("updatekey: " + key + " from table: " + table);
  }

  Map<String, AttributeValueUpdate> attributes = new HashMap<>(values.size());
  for (Entry<String, ByteIterator> val : values.entrySet()) {
    AttributeValue v = new AttributeValue(val.getValue().toString());
    attributes.put(val.getKey(), new AttributeValueUpdate().withValue(v).withAction("PUT"));
  }

  UpdateItemRequest req = new UpdateItemRequest(table, createPrimaryKey(key), attributes);

  try {
    dynamoDB.updateItem(req);
  } catch (AmazonServiceException ex) {
    LOGGER.error(ex);
    return Status.ERROR;
  } catch (AmazonClientException ex) {
    LOGGER.error(ex);
    return CLIENT_ERROR;
  }
  return Status.OK;
}

With the UpdateExpression-based implementation:

@Override
public Status update(String table, String key, Map<String, ByteIterator> values) {
  if (LOGGER.isDebugEnabled()) {
    LOGGER.debug("updatekey: " + key + " from table: " + table);
  }

  StringBuilder updateExp = new StringBuilder("SET ");
  Map<String, String> attrNames = new HashMap<>();
  Map<String, AttributeValue> attrValues = new HashMap<>();
  boolean first = true;
  for (Map.Entry<String, ByteIterator> entry : values.entrySet()) {
    String attr = entry.getKey();
    if (!first) {
      updateExp.append(", ");
    }
    String attrName = "#" + attr;
    String valueName = ":" + attr;
    updateExp.append(attrName).append(" = ").append(valueName);
    attrNames.put(attrName, attr);
    attrValues.put(valueName, new AttributeValue(entry.getValue().toString()));
    first = false;
  }

  UpdateItemRequest req = new UpdateItemRequest()
      .withTableName(table)
      .withKey(createPrimaryKey(key))
      .withUpdateExpression(updateExp.toString())
      .withExpressionAttributeNames(attrNames)
      .withExpressionAttributeValues(attrValues);

  try {
    dynamoDB.updateItem(req);
  } catch (AmazonServiceException ex) {
    LOGGER.error(ex);
    return Status.ERROR;
  } catch (AmazonClientException ex) {
    LOGGER.error(ex);
    return CLIENT_ERROR;
  }
  return Status.OK;
}

Step 3: Run the test

Each test run has two phases: Load Phase (populate test data) and Run Phase (execute the workload). The examples below use a 1 GB dataset (1 million records), 128 concurrent threads, and the read-only workload.

To test different data volumes, adjust the recordcount and operationcount parameters.

Load Phase — populate test data

# -s: Displays status updates during loading.
# -P workloads/workload_read_only: Specifies the workload file.
# -P /path/to/dynamodb.properties: Specifies the database connection configuration.
# -p recordcount=1000000: Total number of records to load (1 GB).
# -p operationcount=1000000: Total number of operations.
# -threads 128: Number of concurrent threads.

nohup ./bin/ycsb load dynamodb -s \
  -P workloads/workload_read_only \
  -P /path/to/dynamodb.properties \
  -p recordcount=1000000 \
  -p operationcount=1000000 \
  -threads 128 \
  > load.log 2>&1 &

Run Phase — execute the workload

After loading completes, run the workload:

nohup ./bin/ycsb run dynamodb -s \
  -P workloads/workload_read_only \
  -P /path/to/dynamodb.properties \
  -p recordcount=1000000 \
  -p operationcount=1000000 \
  -threads 128 \
  > run.log 2>&1 &

Read the results

When the Run Phase completes, run.log contains a summary block at the end. The key metric is Throughput(ops/sec) under [OVERALL]. To find the peak OPS, run the same workload at different concurrency levels and compare the throughput values.

If you reproduce the tests with the same environment configuration, your peak throughput should be comparable to the values in the Test result summary table. Significant deviations may indicate resource contention, network latency, or a configuration mismatch — verify your cluster specifications, client placement, and connection settings.