Oldest active queries, also known as longest running queries, is a list of the queries currently active in your database, sorted by how long they've been running. Gaining insight into these queries can help identify causes of system latency and high CPU usage as they are happening.
Cloud Spanner provides a built-in table,SPANNER_SYS.OLDEST_ACTIVE_QUERIES
, that
lists running queries, including queries containing DML statements, sorted by
start time, in ascending order. It does not include change stream queries.
If there is currently a large number of queries running, the results may be
limited to a subset of total queries due to the memory constraints the system
enforces on the collection of this data. Therefore, Spanner provides
an additional table, SPANNER_SYS.ACTIVE_QUERIES_SUMMARY
, that shows summary
statistics for all active queries (except for change stream queries). You can
retrieve information from both of these built-in tables using SQL statements.
In this article, we'll describe both tables, show some example queries that use these tables and, finally, demonstrate how to use them to help mitigate issues caused by active queries.
Availability
SPANNER_SYS
data is available only through SQL interfaces; for example:
A database's Query page in the Google Cloud console
The
gcloud spanner databases execute-sql
commandThe
executeQuery
API
Other single read methods that Spanner provides do not support
SPANNER_SYS
.
OLDEST_ACTIVE_QUERIES
SPANNER_SYS.OLDEST_ACTIVE_QUERIES
returns a list of active queries sorted by
the start time. If there is currently a large number of queries running, the
results may be limited to a subset of total queries due to the memory
constraints Spanner enforces on the collection of this data. To view
summary statistics for all active queries, see
ACTIVE_QUERIES_SUMMARY
.
Table schema
Column name | Type | Description |
---|---|---|
START_TIME |
TIMESTAMP |
Start time of the query. |
TEXT_FINGERPRINT |
INT64 |
Fingerprint is a hash of the operations involved in the transaction. |
TEXT |
STRING |
The query statement text. |
TEXT_TRUNCATED |
BOOL |
True if the query text in the TEXT field is truncated; Otherwise, false. |
SESSION_ID |
STRING |
The ID of the session that is executing the query. Deleting the session ID will cancel the query. |
Example queries
You can run the following example SQL statements using the client
libraries, the
gcloud
command-line
tool, or the Google Cloud console.
Listing oldest running queries
The following query returns a list of oldest running queries sorted by the start time of the query.
SELECT start_time,
text_fingerprint,
text,
text_truncated,
session_id
FROM spanner_sys.oldest_active_queries
ORDER BY start_time ASC;
start_time | text_fingerprint | text | text_truncated | session_id |
---|---|---|---|---|
2020-07-18T07:52:28.225877Z | -3426560921851907385 | SELECT a.SingerId, a.AlbumId, a.TrackId, b.SingerId as b_id, b.AlbumId as b_albumid, b.TrackId as b_trackId FROM Songs as a CROSS JOIN Songs as b; | False | ACjbPvYsucrtcffHrRK6aObeIjZf12tSUwOsim-g1WC3IhqF4epzICCQR3GCHw |
2020-07-18T07:54:08.622081Z | -9206690983832919848 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE STARTS_WITH(s.FirstName, 'FirstName') LIMIT 1000000; | False | ACjbPvaF3yKiNfxXFod2LPoFaXjKR759Bw1o34206vv0t7eOrD3wxZhu8U6ohQ |
2020-07-18T07:54:08.631744Z | -105437553161169030 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE a.SingerId > 7 LIMIT 1000000; | False | ACjbPvanq3MesDNT98t64KdKAz3TlDZoCC-zgW-FJn91cJHuczQ_cOFN_Hdflw |
2020-07-18T07:54:08.720011Z | -9206690983832919848 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE STARTS_WITH(s.FirstName, 'FirstName') LIMIT 1000000; | False | ACjbPvYIE2QHkhnmMXuAGpB4inK7yMnQjmYgQ9FoygKNaB5KCXu7Sf7f9aghYw |
2020-07-18T07:54:08.731006Z | 6561582859583559006 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE a.SingerId > 10 LIMIT 1000000; | False | ACjbPvYNZ06N2YyvwV0YMlSRBNDtXBqZEK-iAAyPFnFcTmshPvzWkhr034ud7w |
Listing the top 2 oldest running queries
A slight variation on the preceding query, this example returns the top 2 oldest running queries sorted by the start time of the query.
SELECT start_time,
text_fingerprint,
text,
text_truncated,
session_id
FROM spanner_sys.oldest_active_queries
ORDER BY start_time ASC LIMIT 2;
Query output
start_time | text_fingerprint | text | text_truncated | session_id |
---|---|---|---|---|
2020-07-18T07:52:28.225877Z | -3426560921851907385 | SELECT a.SingerId, a.AlbumId, a.TrackId, b.SingerId as b_id, b.AlbumId as b_albumid, b.TrackId as b_trackId FROM Songs as a CROSS JOIN Songs as b; | False | ACjbPvYsucrtcffHrRK6aObeIjZf12tSUwOsim-g1WC3IhqF4epzICCQR3GCHw |
2020-07-18T07:54:08.622081Z | -9206690983832919848 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE STARTS_WITH(s.FirstName, 'FirstName') LIMIT 1000000; | False | ACjbPvaF3yKiNfxXFod2LPoFaXjKR759Bw1o34206vv0t7eOrD3wxZhu8U6ohQ |
ACTIVE_QUERIES_SUMMARY
As its name suggests, the built-in table, SPANNER_SYS.ACTIVE_QUERIES_SUMMARY
,
shows summary statistics for all active queries. As shown in the following
schema, queries are grouped by age into three buckets, or counters, - older than
one second, older than 10 seconds and older than 100 seconds.
Table schema
Column name | Type | Description |
---|---|---|
ACTIVE_COUNT |
INT64 |
The total number of queries that are currently running. |
OLDEST_START_TIME |
TIMESTAMP |
An upper bound on the start time of the oldest running query. |
COUNT_OLDER_THAN_1S |
INT64 |
The number of queries older than 1 second. |
COUNT_OLDER_THAN_10S |
INT64 |
The number of queries older than 10 seconds. |
COUNT_OLDER_THAN_100S |
INT64 |
The number of queries older than 100 seconds. |
A query can be counted in more than one of these buckets. For example, if a
query has been running for 12 seconds, it will be counted in
COUNT_OLDER_THAN_1S
and COUNT_OLDER_THAN_10S
because it satisfies both
criteria.
Example queries
You can run the following example SQL statements using the client
libraries, the
gcloud
command-line
tool, or the Google Cloud console.
Retrieving a summary of active queries
The following query returns the summary stats about running queries.
SELECT active_count,
oldest_start_time,
count_older_than_1s,
count_older_than_10s,
count_older_than_100s
FROM spanner_sys.active_queries_summary;
Query output
active_count | oldest_start_time | count_older_than_1s | count_older_than_10s | count_older_than_100s |
---|---|---|---|---|
22 | 2020-07-18T07:52:28.225877Z | 21 | 21 | 1 |
Limitations
While the goal is to give you the most comprehensive insights possible, there are some circumstances under which queries are not included in the data returned in these tables.
DML queries (UPDATE/INSERT/DELETE) are not included if they're in the Apply mutations phase.
A query is not included if it is in the middle of restarting due to a transient error.
Queries from overloaded or unresponsive servers are not included.
OLDEST_ACTIVE_QUERIES
can't be used in a read-write transaction. Even in a read-only transaction, it ignores the transaction timestamp and always returns current data as of its execution. In rare cases, it may return anABORTED
error with partial results; in that case, discard the partial results and attempt the query again.
Using active queries data to troubleshoot high CPU utilization
Query statistics and transaction statistics provide useful information when troubleshooting latency in a Spanner database. These tools provide information about the queries that have already completed. However, sometimes it is necessary to know what is currently running in the system. For example, consider the scenario when CPU utilization is quite high and you want to answer the following questions.
- How many queries are running at the moment?
- What are these queries?
- How many queries are running for a long time, that is, greater than 100 seconds?
- Which session is running the query?
With answers to the preceding questions you could decide to take the following action.
- Delete the session executing the query for an immediate resolution.
- Improve the query performance by adding an index.
- Reduce the frequency of the query if it is associated with a periodic background task.
- Identify user/component issuing the query which may not be authorized to execute the query.
In this walkthrough, we will examine our active queries and determine what action, if any, to take.
Retrieving a summary of currently active queries
In our example scenario, we notice higher than normal CPU usage, so we decide to run the following query to return a summary of active queries.
SELECT active_count,
oldest_start_time,
count_older_than_1s,
count_older_than_10s,
count_older_than_100s
FROM spanner_sys.active_queries_summary;
The query yields the following results.
active_count | oldest_start_time | count_older_than_1s | count_older_than_10s | count_older_than_100s |
---|---|---|---|---|
22 |
2020-07-18T07:52:28.225877Z |
21 |
21 |
1 |
It turns out we currently have one query that is running for more that 100 seconds. This is unusual for our database, so we want to investigate further.
Retrieving a list of active queries
We determined in the preceding step that we have a query running for over 100 seconds.To investigate further, we run the following query to return more information about the top 5 oldest running queries.
SELECT start_time,
text_fingerprint,
text,
text_truncated,
session_id
FROM spanner_sys.oldest_active_queries
ORDER BY start_time ASC LIMIT 5;
In this example, we ran the query on Sat 18 Jul 2020 at approximately 12:54:18 AM PDT and it yielded the following results. (You might need to scroll horizontally to see the entire output.)
start_time | text_fingerprint | text | text_truncated | session_id |
---|---|---|---|---|
2020-07-18T07:52:28.225877Z |
-3426560921851907385 |
SELECT a.SingerId, a.AlbumId, a.TrackId, b.SingerId as b_id, b.AlbumId as b_albumid, b.TrackId as b_trackId FROM Songs as a CROSS JOIN Songs as b; |
False |
ACjbPvYsucrtcffHrRK6aObeIjZf12tSUwOsim-g1WC3IhqF4epzICCQR3GCHw |
2020-07-18T07:54:08.622081Z | -9206690983832919848 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE STARTS_WITH(s.FirstName, 'FirstName') LIMIT 1000000; | False | ACjbPvaF3yKiNfxXFod2LPoFaXjKR759Bw1o34206vv0t7eOrD3wxZhu8U6ohQ |
2020-07-18T07:54:08.631744Z | -105437553161169030 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE a.SingerId > 7 LIMIT 1000000; | False | ACjbPvanq3MesDNT98t64KdKAz3TlDZoCC-zgW-FJn91cJHuczQ_cOFN_Hdflw |
2020-07-18T07:54:08.720011Z | -9206690983832919848 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE STARTS_WITH(s.FirstName, 'FirstName') LIMIT 1000000; | False | ACjbPvYIE2QHkhnmMXuAGpB4inK7yMnQjmYgQ9FoygKNaB5KCXu7Sf7f9aghYw |
2020-07-18T07:54:08.731006Z | 6561582859583559006 | SELECT a.SingerId, a.AlbumId, a.TrackId, a.SongName, s.FirstName, s.LastName FROM Songs as a JOIN Singers as s ON s.SingerId = a.SingerId WHERE a.SingerId > 10 LIMIT 1000000; | False | ACjbPvYNZ06N2YyvwV0YMlSRBNDtXBqZEK-iAAyPFnFcTmshPvzWkhr034ud7w |
The oldest query (fingerprint = -3426560921851907385
) is highlighted in the table. It is an expensive CROSS JOIN
. We decide to take action.
Canceling an expensive query
We found a query that was running an expensive CROSS JOIN
so, we
decide to cancel the query. The query results in the preceding step included a
session_id
, which is the ID of the session that is executing the query. We can
therefore run the following gcloud spanner databases sessions delete
command to delete the session using that ID which, in turn, cancels the query.
gcloud spanner databases sessions delete\
ACjbPvYsucrtcffHrRK6aObeIjZf12tSUwOsim-g1WC3IhqF4epzICCQR3GCHw \
--database=singer_db --instance=test-instance
This walkthrough demonstrates how to use SPANNER_SYS.OLDEST_ACTIVE_QUERIES
and
SPANNER_SYS.ACTIVE_QUERIES_SUMMARY
to analyse our running queries and take
action if necessary on any queries that are contributing to high CPU usage. Of
course, it is always cheaper to avoid expensive operations and to design the
right schema for your use cases. For more information on constructing SQL
statements that run efficiently, see SQL best practices.
What's next
- Learn about other Introspection tools.
- Learn about other information Spanner stores for each database in the database's information schema tables.
- Learn more about SQL best practices for Spanner.