Getting PartiQL statement statistics
Amazon QLDB provides statement execution statistics that can help you optimize your
usage of QLDB by running more efficient PartiQL statements. QLDB returns these
statistics along with the results of the statement. They include metrics that quantify
consumed I/O usage and server-side processing time, which you can use to identify
inefficient statements.
This feature is currently available in the PartiQL editor on the
QLDB console, the QLDB shell, and the latest version of the QLDB driver for all supported languages.
You can also view statement statistics for your query history on the console.
I/O usage
The I/O usage metric describes the number of read I/O requests. If the number of
read I/O requests is higher than expected, it indicates that the statement isn't
optimized, such as the lack of an index. We recommend that you review Optimal query patterns
in the previous topic, Optimizing query performance.
When you run a CREATE INDEX
statement on a non-empty table, the
I/O usage metric includes read requests for the synchronous index creation call
only.
QLDB builds the index for any existing documents in the table
asynchronously. These asynchronous read requests aren't included in the I/O
usage metric from your statement results. Asynchronous read requests are charged
separately and are added to your total read I/Os after the index build is
completed.
Using the QLDB console
To get a statement's read I/O usage by using the QLDB console, do the
following steps:
-
Open the Amazon QLDB console at https://console.aws.amazon.com/qldb.
-
In the navigation pane, choose PartiQL
editor.
-
Choose a ledger from the dropdown list of ledgers.
-
In the query editor window, enter any statement of your choice, and
then choose Run. The following is a query
example.
SELECT * FROM testTable WHERE firstName = 'Jim'
To run a statement, you can also use the keyboard shortcut
Ctrl+Enter for Windows, or
Cmd+Return for macOS. For more
keyboard shortcuts, see PartiQL editor keyboard shortcuts.
-
Below the query editor window, your query results include
read I/Os, which is the number of read requests
that were made by the statement.
You can also view the read I/Os of your query history by doing the following
steps:
-
In navigation pane, choose Recent queries under
PartiQL editor.
-
The Read I/Os column displays the number of read
requests that were made by each statement.
Using the QLDB
driver
To get a statement's I/O usage by using the QLDB driver, call the
getConsumedIOs
operation of the result's stream cursor or
buffered cursor.
The following code examples show how to get read I/Os from the
stream cursor of a statement result.
- Java
-
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import software.amazon.qldb.IOUsage;
import software.amazon.qldb.Result;
IonSystem ionSystem = IonSystemBuilder.standard().build();
IonValue ionFirstName = ionSystem.newString("Jim");
driver.execute(txn -> {
Result result = txn.execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
for (IonValue ionValue : result) {
// User code here to handle results
}
IOUsage ioUsage = result.getConsumedIOs();
long readIOs = ioUsage.getReadIOs();
});
- .NET
-
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
// This is one way of creating Ion values. We can also use a ValueFactory.
// For more details, see: https://docs.aws.amazon.com/qldb/latest/developerguide/driver-cookbook-dotnet.html#cookbook-dotnet.ion
IIonValue ionFirstName = IonLoader.Default.Load("Jim");
await driver.Execute(async txn =>
{
IAsyncResult result = await txn.Execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
// Iterate through stream cursor to accumulate read IOs.
await foreach (IIonValue ionValue in result)
{
// User code here to handle results.
// Warning: It is bad practice to rely on results within a lambda block, unless
// it is to check the state of a result. This is because lambdas are retryable.
}
var ioUsage = result.GetConsumedIOs();
var readIOs = ioUsage?.ReadIOs;
});
To convert to synchronous code, remove the await
and async
keywords, and change the IAsyncResult
type to IResult
.
- Go
-
import (
"context"
"fmt"
"github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)
driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
result, err := txn.Execute("SELECT * FROM testTable WHERE firstName = ?", "Jim")
if err != nil {
panic(err)
}
for result.Next(txn) {
// User code here to handle results
}
ioUsage := result.GetConsumedIOs()
readIOs := *ioUsage.GetReadIOs()
fmt.Println(readIOs)
return nil,nil
})
- Node.js
-
import { IOUsage, ResultReadable, TransactionExecutor } from "amazon-qldb-driver-nodejs";
await driver.executeLambda(async (txn: TransactionExecutor) => {
const result: ResultReadable = await txn.executeAndStreamResults("SELECT * FROM testTable WHERE firstName = ?", "Jim");
for await (const chunk of result) {
// User code here to handle results
}
const ioUsage: IOUsage = result.getConsumedIOs();
const readIOs: number = ioUsage.getReadIOs();
});
- Python
-
def get_read_ios(transaction_executor):
cursor = transaction_executor.execute_statement("SELECT * FROM testTable WHERE firstName = ?", "Jim")
for row in cursor:
# User code here to handle results
pass
consumed_ios = cursor.get_consumed_ios()
read_ios = consumed_ios.get('ReadIOs')
qldb_driver.execute_lambda(lambda txn: get_read_ios(txn))
The following code examples show how to get read I/Os from the
buffered cursor of a statement result. This returns the
total read I/Os from ExecuteStatement
and FetchPage
requests.
- Java
-
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import software.amazon.qldb.IOUsage;
import software.amazon.qldb.Result;
IonSystem ionSystem = IonSystemBuilder.standard().build();
IonValue ionFirstName = ionSystem.newString("Jim");
Result result = driver.execute(txn -> {
return txn.execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
});
IOUsage ioUsage = result.getConsumedIOs();
long readIOs = ioUsage.getReadIOs();
- .NET
-
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
IIonValue ionFirstName = IonLoader.Default.Load("Jim");
IAsyncResult result = await driver.Execute(async txn =>
{
return await txn.Execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
});
var ioUsage = result.GetConsumedIOs();
var readIOs = ioUsage?.ReadIOs;
To convert to synchronous code, remove the await
and async
keywords, and change the IAsyncResult
type to IResult
.
- Go
-
import (
"context"
"fmt"
"github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
result, err := txn.Execute("SELECT * FROM testTable WHERE firstName = ?", "Jim")
if err != nil {
return nil, err
}
return txn.BufferResult(result)
})
if err != nil {
panic(err)
}
qldbResult := result.(*qldbdriver.BufferedResult)
ioUsage := qldbResult.GetConsumedIOs()
readIOs := *ioUsage.GetReadIOs()
fmt.Println(readIOs)
- Node.js
-
import { IOUsage, Result, TransactionExecutor } from "amazon-qldb-driver-nodejs";
const result: Result = await driver.executeLambda(async (txn: TransactionExecutor) => {
return await txn.execute("SELECT * FROM testTable WHERE firstName = ?", "Jim");
});
const ioUsage: IOUsage = result.getConsumedIOs();
const readIOs: number = ioUsage.getReadIOs();
- Python
-
cursor = qldb_driver.execute_lambda(
lambda txn: txn.execute_statement("SELECT * FROM testTable WHERE firstName = ?", "Jim"))
consumed_ios = cursor.get_consumed_ios()
read_ios = consumed_ios.get('ReadIOs')
The stream cursor is stateful because it paginates the result set.
Therefore, the getConsumedIOs
and
getTimingInformation
operations return the accumulated
metrics from the time that you call them.
The buffered cursor buffers the result set in memory and returns the total
accumulated metrics.
The timing information metric describes the server-side processing time in
milliseconds. Server-side processing time is defined as the amount of time that
QLDB spends on processing a statement. This doesn't include time spent on network
calls or pauses. This metric disambiguates the processing time on the QLDB service
side from the processing time on the client side.
To get a statement's timing information by using the QLDB console, do the
following steps:
-
Open the Amazon QLDB console at https://console.aws.amazon.com/qldb.
-
In the navigation pane, choose PartiQL
editor.
-
Choose a ledger from the dropdown list of ledgers.
-
In the query editor window, enter any statement of your choice, and
then choose Run. The following is a query
example.
SELECT * FROM testTable WHERE firstName = 'Jim'
To run a statement, you can also use the keyboard shortcut
Ctrl+Enter for Windows, or
Cmd+Return for macOS. For more
keyboard shortcuts, see PartiQL editor keyboard shortcuts.
-
Below the query editor window, your query results include
server-side latency, which is the amount of
time between when QLDB received the statement request, and when it
sent the response. This is a subset of the total query duration.
You can also view the timing information of your query history by doing the
following steps:
-
In navigation pane, choose Recent queries under
PartiQL editor.
-
The Execution time (ms) column displays this timing
information for each statement.
To get a statement's timing information by using the QLDB driver, call the
getTimingInformation
operation of the result's stream cursor or
buffered cursor.
The following code examples show how to get the processing time from the
stream cursor of a statement result.
- Java
-
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import software.amazon.qldb.Result;
import software.amazon.qldb.TimingInformation;
IonSystem ionSystem = IonSystemBuilder.standard().build();
IonValue ionFirstName = ionSystem.newString("Jim");
driver.execute(txn -> {
Result result = txn.execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
for (IonValue ionValue : result) {
// User code here to handle results
}
TimingInformation timingInformation = result.getTimingInformation();
long processingTimeMilliseconds = timingInformation.getProcessingTimeMilliseconds();
});
- .NET
-
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
IIonValue ionFirstName = IonLoader.Default.Load("Jim");
await driver.Execute(async txn =>
{
IAsyncResult result = await txn.Execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
// Iterate through stream cursor to accumulate processing time.
await foreach(IIonValue ionValue in result)
{
// User code here to handle results.
// Warning: It is bad practice to rely on results within a lambda block, unless
// it is to check the state of a result. This is because lambdas are retryable.
}
var timingInformation = result.GetTimingInformation();
var processingTimeMilliseconds = timingInformation?.ProcessingTimeMilliseconds;
});
To convert to synchronous code, remove the await
and async
keywords, and change the IAsyncResult
type to IResult
.
- Go
-
import (
"context"
"fmt"
"github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)
driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
result, err := txn.Execute("SELECT * FROM testTable WHERE firstName = ?", "Jim")
if err != nil {
panic(err)
}
for result.Next(txn) {
// User code here to handle results
}
timingInformation := result.GetTimingInformation()
processingTimeMilliseconds := *timingInformation.GetProcessingTimeMilliseconds()
fmt.Println(processingTimeMilliseconds)
return nil, nil
})
- Node.js
-
import { ResultReadable, TimingInformation, TransactionExecutor } from "amazon-qldb-driver-nodejs";
await driver.executeLambda(async (txn: TransactionExecutor) => {
const result: ResultReadable = await txn.executeAndStreamResults("SELECT * FROM testTable WHERE firstName = ?", "Jim");
for await (const chunk of result) {
// User code here to handle results
}
const timingInformation: TimingInformation = result.getTimingInformation();
const processingTimeMilliseconds: number = timingInformation.getProcessingTimeMilliseconds();
});
- Python
-
def get_processing_time_milliseconds(transaction_executor):
cursor = transaction_executor.execute_statement("SELECT * FROM testTable WHERE firstName = ?", "Jim")
for row in cursor:
# User code here to handle results
pass
timing_information = cursor.get_timing_information()
processing_time_milliseconds = timing_information.get('ProcessingTimeMilliseconds')
qldb_driver.execute_lambda(lambda txn: get_processing_time_milliseconds(txn))
The following code examples show how to get the processing time from the
buffered cursor of a statement result. This returns the
total processing time from ExecuteStatement
and FetchPage
requests.
- Java
-
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import software.amazon.qldb.Result;
import software.amazon.qldb.TimingInformation;
IonSystem ionSystem = IonSystemBuilder.standard().build();
IonValue ionFirstName = ionSystem.newString("Jim");
Result result = driver.execute(txn -> {
return txn.execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
});
TimingInformation timingInformation = result.getTimingInformation();
long processingTimeMilliseconds = timingInformation.getProcessingTimeMilliseconds();
- .NET
-
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB.Driver;
using IAsyncResult = Amazon.QLDB.Driver.IAsyncResult;
IIonValue ionFirstName = IonLoader.Default.Load("Jim");
IAsyncResult result = await driver.Execute(async txn =>
{
return await txn.Execute("SELECT * FROM testTable WHERE firstName = ?", ionFirstName);
});
var timingInformation = result.GetTimingInformation();
var processingTimeMilliseconds = timingInformation?.ProcessingTimeMilliseconds;
To convert to synchronous code, remove the await
and async
keywords, and change the IAsyncResult
type to IResult
.
- Go
-
import (
"context"
"fmt"
"github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver"
)
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
result, err := txn.Execute("SELECT * FROM testTable WHERE firstName = ?", "Jim")
if err != nil {
return nil, err
}
return txn.BufferResult(result)
})
if err != nil {
panic(err)
}
qldbResult := result.(*qldbdriver.BufferedResult)
timingInformation := qldbResult.GetTimingInformation()
processingTimeMilliseconds := *timingInformation.GetProcessingTimeMilliseconds()
fmt.Println(processingTimeMilliseconds)
- Node.js
-
import { Result, TimingInformation, TransactionExecutor } from "amazon-qldb-driver-nodejs";
const result: Result = await driver.executeLambda(async (txn: TransactionExecutor) => {
return await txn.execute("SELECT * FROM testTable WHERE firstName = ?", "Jim");
});
const timingInformation: TimingInformation = result.getTimingInformation();
const processingTimeMilliseconds: number = timingInformation.getProcessingTimeMilliseconds();
- Python
-
cursor = qldb_driver.execute_lambda(
lambda txn: txn.execute_statement("SELECT * FROM testTable WHERE firstName = ?", "Jim"))
timing_information = cursor.get_timing_information()
processing_time_milliseconds = timing_information.get('ProcessingTimeMilliseconds')
The stream cursor is stateful because it paginates the result set.
Therefore, the getConsumedIOs
and
getTimingInformation
operations return the accumulated
metrics from the time that you call them.
The buffered cursor buffers the result set in memory and returns the total
accumulated metrics.
To learn how to query the system catalog, proceed to Querying the system catalog.