Hologres is a real-time data warehouse engine compatible with the PostgreSQL protocol. It supports real-time writes, updates, and Online Analytical Processing (OLAP) on petabytes of data, along with high-concurrency, low-latency online data services.
This topic explains how to run performance tests for data writes, updates, and point queries using holo-e2e-performance-tool, and provides reference benchmark results on a 64-vCPU instance.
Test scenarios
Scenario | What is tested | Storage formats |
Data write | Write throughput to row-oriented, column-oriented, and row-column hybrid tables | All three |
Data update | Update throughput for global and partial updates on tables with a primary key | All three |
Point query | Point query performance filtering by primary key | Row-oriented and row-column hybrid only |
Column-oriented tables are not suitable for point queries and are excluded from that scenario.
How it works
The three test scenarios build on each other:
Write test -- Populate a table with test data.
Update test -- Reuse the same table to measure update throughput (keep the table from step 1).
Point query test -- Query the same table by primary key.
Write and update mechanics
The tool supports two write modes:
Fixed Copy mode -- Uses the PostgreSQL
COPYstatement, optimized with Fixed Plan for faster SQL execution.Insert mode -- Uses standard
INSERTstatements, also optimized with Fixed Plan.
In both modes, the tool automatically adds two columns to the table:
Column | Role | Behavior |
| Primary key and distribution key | Starts at 1 and increments with each row |
| Segment key | Set to the current timestamp on each write |
For every configured TEXT column, the tool writes a string of the specified length with the id value appended. The test stops when the target row count or duration is reached.
During updates, the same incrementing id pattern is used. For partial updates, only a subset of TEXT columns is rewritten.
Point query mechanics
The tool supports two query modes:
Asynchronous mode -- The point query API is non-blocking. Multiple requests are batched into a single SQL statement, maximizing throughput. Best for high-throughput scenarios such as Flink-based real-time table joins.
Synchronous mode -- The point query API is blocking. Each request maps to one SQL statement and completes before the next starts. Best for latency-sensitive scenarios.
During the test, the tool randomly selects a primary key within the configured range and performs a point query. The test stops when the target duration is reached.
Prerequisites
Before you begin, prepare the following:
A Hologres instance (dedicated, pay-as-you-go). The benchmark results in this topic use a 64-vCPU, 256 GB memory instance. Use a newly created instance rather than one that has been upgraded or downgraded, to minimize variables that affect results.
An Elastic Compute Service (ECS) instance to serve as the test client:
Recommended instance type:
ecs.g6.4xlargeOperating system: Alibaba Cloud Linux 3.2104 LTS 64-bit
Storage: enterprise SSD (ESSD)
The ECS instance must be in the same region, Virtual Private Cloud (VPC), and zone as the Hologres instance
A database created on the Hologres instance. For details, see Create a database.
Java Development Kit (JDK) 11 installed on the ECS instance. For details, see Manually deploy OpenJDK.
The holo-e2e-performance-tool JAR file, uploaded to the ECS instance. For upload instructions, see Use Workbench to upload or download files.
The ECS specifications above are recommendations, not requirements. During tests, monitor the ECS CPU and network bandwidth to confirm the client is not a bottleneck.
About the test tool
holo-e2e-performance-tool is an open-source tool developed by the Hologres team. It integrates table creation, test data generation, and performance measurement into a single JAR, so there is no need to prepare test data separately. The primary key for generated data is a series of consecutive integers, which guarantees full hit rates during update and point query tests.
Run a data write test
Step 1: Create the configuration file
On the ECS instance, create a file named test_insert.conf with the following content:
# Connection configuration
holoClient.jdbcUrl=jdbc:hologres://<ENDPOINT>:<PORT>/<DBNAME>
holoClient.username=<AccessKey_ID>
holoClient.password=<AccessKey_Secret>
holoClient.writeThreadSize=100
# Write configuration
put.threadSize=8
put.testByTime=false
put.rowNumber=200000000
put.testTime=600000
# Table configuration
put.tableName=kv_test
put.columnCount=20
put.columnSize=20
put.orientation=row
# Other configurations
put.createTableBeforeRun=true
put.deleteTableAfterDone=false
put.vacuumTableBeforeRun=falseThis example creates a row-oriented table. To test a column-oriented or row-column hybrid table, changeorientationtocolumnorrow,column.
Replace the placeholders with your actual values:
Placeholder | Description | Where to find it |
| VPC domain name of the Hologres instance | Instance Details > Network Information in the Hologres console |
| Port number of the Hologres instance | Same location as above |
| Name of the test database | The database you created in the prerequisites |
| AccessKey ID of your Alibaba Cloud account | AccessKey Management page |
| AccessKey secret of your Alibaba Cloud account | Same location as above |
Configuration parameters
Module | Parameter | Description | Notes |
Connection |
| Java Database Connectivity (JDBC) connection string. Format: | Use the VPC domain name for |
| AccessKey ID | ||
| AccessKey secret | ||
| Number of write threads per Holo Client. Each thread uses one connection. Applies to Insert mode only. | In Fixed Copy mode, the connection count equals | |
Write |
| Number of data generation threads | In Fixed Copy mode, each thread uses one connection (total connections = |
| Controls whether the test runs for a fixed duration or a fixed number of rows |
| |
| Target row count. Applies only when | ||
| Target duration in milliseconds. Applies only when | ||
Table |
| Name of the test table | |
| Number of TEXT columns in the table | ||
| Character length of each TEXT column | ||
| Storage format of the table |
| |
Other |
| Whether to create the table before the test starts |
|
| Whether to delete the table after the test completes | Set to | |
| Whether to run |
|
Step 2: Run the test
# Fixed Copy mode
java -jar holo-e2e-performance-tool-1.0.0.jar test_insert.conf FIXED_COPY
# Or, Insert mode
java -jar holo-e2e-performance-tool-1.0.0.jar test_insert.conf INSERTStep 3: View the results
cat result.csvSee Result fields for the output format.
Keep the test table (deleteTableAfterDone=false) if you plan to run update or point query tests next.
Run a data update test
The update test reuses the table populated during the write test. Two update types are supported: global update (all columns) and partial update (a subset of columns).
Global update
Create a file named
test_update.conf. Use the same configuration as the write test, but changecreateTableBeforeRuntofalse:The only change from the write test configuration is
createTableBeforeRun=false. This preserves the existing table and data.# Connection configuration holoClient.jdbcUrl=jdbc:hologres://<ENDPOINT>:<PORT>/<DBNAME> holoClient.username=<AccessKey_ID> holoClient.password=<AccessKey_Secret> holoClient.writeThreadSize=100 # Write configuration put.threadSize=8 put.testByTime=false put.rowNumber=200000000 put.testTime=600000 # Table configuration put.tableName=kv_test put.columnCount=20 put.columnSize=20 put.orientation=row # Other configurations put.createTableBeforeRun=false put.deleteTableAfterDone=false put.vacuumTableBeforeRun=falseRun the test:
java -jar holo-e2e-performance-tool-1.0.0.jar test_update.conf FIXED_COPYView the results:
cat result.csv
Partial update
A partial update writes to a subset of the TEXT columns. The writeColumnCount parameter controls how many columns are updated.
Create a file named
test_update_part.conf. Add thewriteColumnCountparameter to the table configuration section:writeColumnCount=10updates 10 of the 20 TEXT columns (50%). Adjust this ratio to match your workload.# Connection configuration holoClient.jdbcUrl=jdbc:hologres://<ENDPOINT>:<PORT>/<DBNAME> holoClient.username=<AccessKey_ID> holoClient.password=<AccessKey_Secret> holoClient.writeThreadSize=100 # Write configuration put.threadSize=8 put.testByTime=false put.rowNumber=200000000 put.testTime=600000 # Table configuration put.tableName=kv_test put.columnCount=20 put.columnSize=20 put.writeColumnCount=10 put.orientation=row # Other configurations put.createTableBeforeRun=false put.deleteTableAfterDone=false put.vacuumTableBeforeRun=falseRun the test:
java -jar holo-e2e-performance-tool-1.0.0.jar test_update_part.conf FIXED_COPYView the results:
cat result.csv
Run a point query test
Step 1: Create the configuration file
Create a file named test_get.conf:
# Connection configuration
holoClient.jdbcUrl=jdbc:hologres://<ENDPOINT>:<PORT>/<DBNAME>
holoClient.username=<AccessKey_ID>
holoClient.password=<AccessKey_Secret>
holoClient.readThreadSize=32
# Test configuration
get.threadSize=8
get.testTime=300000
get.tableName=kv_test
get.async=true
get.vacuumTableBeforeRun=true
get.keyRangeParams=L1-200000000
# Table initialization configuration (used only in PREPARE_GET_DATA mode)
prepareGetData.rowNumber=200000000
prepareGetData.orientation=row
put.columnCount=20
put.columnSize=20This example configures asynchronous point queries on a row-oriented table. To test synchronous queries, setget.async=falseand adjustthreadSizeandreadThreadSizeaccordingly (see the parameter table below).
Point query parameters
Module | Parameter | Description | Notes |
Connection |
| Number of query connections | Asynchronous mode: set to 2-4x |
Test |
| Number of threads generating query requests | Asynchronous mode: 8 is a good default on a 64-vCPU instance. Synchronous mode: increase to 500 for higher throughput. |
| Test duration in milliseconds | ||
| Target table name | ||
| Whether to use asynchronous point queries |
| |
| Whether to run | Running | |
| Primary key range for queries. Format: |
| |
Table init (PREPARE_GET_DATA mode only) |
| Rows to generate | |
| Storage format |
| |
| Number of TEXT columns | ||
| Character length per column |
Step 2: Run the test
If the table already has data from the write or update tests, skip data preparation and run the point query directly:
# (Optional) Prepare data -- only if the table has no data
java -jar holo-e2e-performance-tool-1.0.0.jar test_get.conf PREPARE_GET_DATA
# Run the point query test
java -jar holo-e2e-performance-tool-1.0.0.jar test_get.conf GETTo use your own business data instead of generated data, the table must have a single-column primary key of the INT or BIGINT type with consecutive values.
Step 3: View the results
cat result.csvResult fields
All test modes write results to result.csv in the current directory. The file contains the following fields:
Field | Description |
| Test start time |
| Test end time |
| Total rows processed |
| Average QPS over the last 1 minute |
| Average QPS over the last 5 minutes |
| Average QPS over the last 15 minutes |
| Average latency (ms). Collected in Insert and GET modes only. |
| P99 latency (ms). Collected in Insert and GET modes only. |
| P999 latency (ms). Collected in Insert and GET modes only. |
| Hologres instance version |
Benchmark results
The following results were collected on a Hologres V3.0.22 instance with 64 vCPUs, using JDK 11 and Fixed Copy mode. Each scenario used 200 million rows with a column size of 20 characters. The recommended total data volume for a 64-vCPU instance is 40 million to 400 million rows. Column counts tested: 20, 50, and 100. QPS values are taken from the qps1 metric and latency values from latencyMean.
Instance type, table schema, data volume, and concurrency all affect performance significantly. Use these numbers as a baseline and adjust concurrency across multiple test runs for your specific configuration. If CPU utilization is not fully saturated after a test run, increase concurrency to find the optimal throughput.
Choose a storage format
Before reviewing the numbers, use this guide to select the right storage format for your workload:
Workload | Recommended format | Why |
Write-heavy, no point queries | Row-oriented | Highest write and update throughput (roughly 3x faster than column-oriented) |
Mixed writes and point queries | Row-column hybrid | Supports both high-throughput writes and low-latency point queries |
Analytical queries on wide tables | Column-oriented | Optimized for column scans, but write throughput is lower |
Partial column updates | Row-oriented | Partial updates on row-oriented tables achieve the highest QPS |
20 columns
Storage format | Scenario | Concurrency | QPS (x 10,000) | Latency (ms) |
Row-oriented | Write (Fixed Copy) | threadSize=8 | 88.3 | -- |
Global update (Fixed Copy) | threadSize=8 | 91.1 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 136.7 | -- | |
Point query (async) | threadSize=8, readThreadSize=32 | 43.6 | 8.60 | |
Point query (sync) | threadSize=100, readThreadSize=100 | 26.1 | 7.88 | |
Column-oriented | Write (Fixed Copy) | threadSize=8 | 29.9 | -- |
Global update (Fixed Copy) | threadSize=8 | 18.5 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 12.8 | -- | |
Row-column hybrid | Write (Fixed Copy) | threadSize=8 | 25.0 | -- |
Global update (Fixed Copy) | threadSize=8 | 17.3 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 17.5 | -- | |
Point query (async) | threadSize=8, readThreadSize=32 | 40.4 | 8.75 | |
Point query (sync) | threadSize=100, readThreadSize=100 | 27.0 | 8.12 |
50 columns
Storage format | Scenario | Concurrency | QPS (x 10,000) | Latency (ms) |
Row-oriented | Write (Fixed Copy) | threadSize=8 | 41.9 | -- |
Global update (Fixed Copy) | threadSize=8 | 38.2 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 69.8 | -- | |
Point query (async) | threadSize=8, readThreadSize=32 | 35.7 | 10.20 | |
Point query (sync) | threadSize=100, readThreadSize=100 | 24.0 | 9.28 | |
Column-oriented | Write (Fixed Copy) | threadSize=8 | 15.5 | -- |
Global update (Fixed Copy) | threadSize=8 | 11.7 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 6.4 | -- | |
Row-column hybrid | Write (Fixed Copy) | threadSize=8 | 13.7 | -- |
Global update (Fixed Copy) | threadSize=8 | 10.5 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 11.6 | -- | |
Point query (async) | threadSize=8, readThreadSize=32 | 31.5 | 12.29 | |
Point query (sync) | threadSize=100, readThreadSize=100 | 24.2 | 10.00 |
100 columns
Storage format | Scenario | Concurrency | QPS (x 10,000) | Latency (ms) |
Row-oriented | Write (Fixed Copy) | threadSize=8 | 24.4 | -- |
Global update (Fixed Copy) | threadSize=8 | 22.5 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 38.1 | -- | |
Point query (async) | threadSize=8, readThreadSize=32 | 26.6 | 14.34 | |
Point query (sync) | threadSize=100, readThreadSize=100 | 21.4 | 12.12 | |
Column-oriented | Write (Fixed Copy) | threadSize=8 | 8.4 | -- |
Global update (Fixed Copy) | threadSize=8 | 6.9 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 3.1 | -- | |
Row-column hybrid | Write (Fixed Copy) | threadSize=8 | 7.6 | -- |
Global update (Fixed Copy) | threadSize=8 | 5.3 | -- | |
Partial update (Fixed Copy) | threadSize=8 | 6.8 | -- | |
Point query (async) | threadSize=8, readThreadSize=32 | 25.0 | 15.66 | |
Point query (sync) | threadSize=100, readThreadSize=100 | 21.4 | 13.79 |
Key takeaways
Row-oriented tables deliver the highest write throughput -- roughly 3x faster than column-oriented tables for the same column count.
Partial updates outperform global updates on row-oriented tables because fewer columns are rewritten. On column-oriented and row-column hybrid tables, global updates may match or exceed partial update throughput.
Point query latency is similar between row-oriented and row-column hybrid tables, typically under 10 ms for 20-column tables.
Asynchronous point queries achieve higher QPS than synchronous queries, but at slightly higher latency.
Write and update throughput decreases as column count increases -- a 100-column row-oriented table achieves roughly 28% of the write QPS of a 20-column table. Point query throughput is less affected, retaining 50-60% of QPS at 100 columns compared to 20 columns.
Troubleshooting
ECS client becomes the bottleneck
Symptom: QPS plateaus despite low CPU utilization on the Hologres instance.
Cause: The ECS instance running the test tool has reached its CPU or network bandwidth limit.
Solution: Monitor ECS CPU and network metrics during the test. If either is saturated, upgrade to a larger ECS instance type or reduce threadSize.
Lower-than-expected QPS
Symptom: QPS is significantly lower than the benchmark results listed above.
Possible causes and solutions:
Insufficient concurrency -- If Hologres CPU utilization is low, increase
threadSize(for writes and updates) orreadThreadSize(for point queries).Instance has been upgraded or downgraded -- Performance characteristics differ between fresh and resized instances. Create a new instance for benchmarking.
Network latency -- Confirm that the ECS instance and the Hologres instance are in the same VPC and zone.
Data skew -- If using custom data, uneven distribution key values can concentrate load on a subset of shards.
Connection errors
Symptom: The test tool fails with connection-related exceptions.
Solution: Verify the JDBC connection string, AccessKey ID, and AccessKey secret. Confirm that the Hologres instance allows connections from the ECS instance's VPC.