What’s New in TiDB 8.5.7: Partial Indexes, CPU Scheduling & Connection Limits

Kabilesh PR
Nov 2, 2026
6
Mins to Read
All
What’s New in TiDB 8.5.7
What’s New in TiDB 8.5.7

The TiDB 8.5 Long-Term Support (LTS) series is designed to deliver reliable scalability, high performance, and wire-level MySQL compatibility for mission-critical systems. As part of PingCAP’s regular maintenance cycle, the 8.5.7 release brings a focused set of features aimed at solving real DBA challenges, ranging from write-amplification reduction to connection throttling and smarter hotspot rebalancing.

Released on July 9, 2026, TiDB 8.5.7 includes partial index support, CPU-aware read hotspot scheduling, per-user connection limits, and execution plan push-downs for ORDER BY ... LIMIT queries.

Let’s look at the key features and operational adjustments in detail.

1. Partial Indexes

One of the most requested SQL features in TiDB 8.5.7 is support for partial indexes.

A partial index indexes only the rows that satisfy a specific WHERE predicate. On large tables where active or queryable data represents only a fraction of total records (such as open orders, pending tickets, or unprocessed events), standard secondary indexes waste storage and drive up write amplification. Partial indexes solve this by keeping index trees small, cache-friendly, and lightweight to maintain.

Example Scenario

Consider an orders table where most orders are historical and completed, but applications primarily query active orders:

mysql> show create table orders\G
*************************** 1. row ***************************
       Table: orders
Create Table: CREATE TABLE `orders` (
  `id` bigint NOT NULL,
  `customer_id` bigint DEFAULT NULL,
  `status` varchar(20) DEFAULT NULL,
  `amount` decimal(12,2) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
1 row in set (0.00 sec)

Without an index covering the status, fetching active records for a single customer requires scanning the entire table:

mysql> explain SELECT * FROM orders WHERE status = 'ACTIVE'   AND customer_id = 1001\G
*************************** 1. row ***************************
           id: TableReader_7
      estRows: 0.01
         task: root
access object: 
operator info: data:Selection_6
*************************** 2. row ***************************
           id: └─Selection_6
      estRows: 0.01
         task: cop[tikv]
access object: 
operator info: eq(test.orders.customer_id, 1001), eq(test.orders.status, "ACTIVE")
*************************** 3. row ***************************
           id:   └─TableFullScan_5
      estRows: 10000.00
         task: cop[tikv]
access object: table:orders
operator info: keep order:false, stats:pseudo
3 rows in set (0.01 sec)

Instead of building a massive secondary index covering every single status, you can create a targeted partial index:

mysql> CREATE INDEX idx_active_customer
    -> ON orders(customer_id)
    -> WHERE status = 'ACTIVE';
Query OK, 0 rows affected (3.95 sec)

With the partial index in place, the query optimizer checks whether the query's filter conditions match or imply the partial index definition. When they match, it switches to an IndexRangeScan:

mysql> explain SELECT * FROM orders WHERE status = 'ACTIVE'   AND customer_id = 1001\G
*************************** 1. row ***************************
           id: IndexLookUp_8
      estRows: 0.01
         task: root
access object: 
operator info: 
*************************** 2. row ***************************
           id: ├─IndexRangeScan_5(Build)
      estRows: 10.00
         task: cop[tikv]
access object: table:orders, index:idx_active_customer(customer_id)
operator info: range:[1001,1001], keep order:false, stats:pseudo
*************************** 3. row ***************************
           id: └─Selection_7(Probe)
      estRows: 0.01
         task: cop[tikv]
access object: 
operator info: eq(test.orders.status, "ACTIVE")
*************************** 4. row ***************************
           id:   └─TableRowIDScan_6
      estRows: 10.00
         task: cop[tikv]
access object: table:orders
operator info: keep order:false, stats:pseudo
4 rows in set (0.01 sec)

Storage & Index Write-Path Comparison

How the predicate filter eliminates 95% of secondary index writes and memory bloat

TRADITIONAL INDEXING Table Mutations 100% of rows (all statuses) Writes all Full Secondary Index Indexes 100% of data (bloated tree) 100% IO Large TiKV SSTs & Cache Waste High write amplification • Low cache hits TiDB 8.5.7 PARTIAL INDEX TABLE: orders status='ACTIVE' (5%) other status (95%) Predicate Evaluator WHERE status='ACTIVE' Evaluates during table write MATCH (5%) Targeted Partial Index ✓ Only 5% of records written Lean TiKV SSTs 95% Storage Saved NO MATCH (95%) ✕ Write Bypassed: Zero secondary index keys created Zero disk writes • Zero CPU spent on maintenance

TiDB uses a partial index when the predicates in the query match or imply the predicate defined by the partial index. In other words, the optimizer needs to be able to establish that the rows relevant to the query are covered by the partial index. TiDB 8.5.7 supports creating partial indexes using CREATE INDEX, ALTER TABLE ... ADD INDEX, and index definitions in CREATE TABLE.

2. CPU-Aware Hot Region Scheduling

Balancing distributed storage nodes is rarely as simple as distributing byte volume or total query rates. Two TiKV stores can receive identical queries per second (QPS), yet experience completely different CPU utilization. A simple single-row point lookup consumes negligible CPU, whereas an aggregation over a wide range takes significantly more processor cycles.

Prior to 8.5.7, Placement Driver (PD) hot Region scheduling based its decisions primarily on query rate and byte throughput. TiDB 8.5.7 introduces TiKV CPU utilization as an additional signal for read hot Region scheduling.

TiKV nodes now track and report CPU time consumed by read hot Regions back to PD through periodic store heartbeats. PD evaluates this data when determining how to balance read replicas across the cluster.

CPU-Driven Hotspot Feedback Loop

Placement Driver (PD) leverages TiKV heartbeats to schedule compute-skewed regions

PD Cluster Coordinator Evaluates: cpu-read-rate min-hot-cpu-rate = 10 (10% core) TiKV Store 01 (Overloaded Core) Throughput: 1,200 QPS (Complex Aggs) CPU Usage: 94% 🔥 Hot Region Leader TiKV Store 02 (Capacity Available) Throughput: 1,200 QPS (Point Lookups) CPU Usage: 22% Target Follower Node ▲ Heartbeat (Reports CPU) ▼ Schedule Peer Transfer Transfer Leader ▶

New CPU Hotspot Metrics

TiDB 8.5.7 adds three new parameters to hot-store statistics:

  • cpu-read-rate: Tracks current CPU consumption generated by read traffic.
  • min-hot-cpu-rate: Sets the threshold required for a read request to register as a hotspot. The default value is 10 (representing 10% of a single CPU core).
  • cpu-rate-rank-step-ratio: Configures how PD ranks and groups CPU-based hotspots into priority tiers for scheduling moves.

You can inspect and adjust these configurations using pd-ctl. This scheduling change is particularly beneficial when:

  • Workloads include a mix of simple key lookups and CPU-heavy analytical reads.
  • TiKV is deployed on heterogeneous hardware nodes with unequal CPU capacity.
  • Cluster dashboards show balanced throughput and QPS, but persistent CPU skew on individual storage nodes.

3. Per-User Connection Limits

Connection spikes from misbehaving application services or rogue batch scripts can exhaust TiDB connection pools, degrading performance for all tenants.

TiDB 8.5.7 introduces the max_user_connections system variable, enabling administrators to set a hard cap on concurrent sessions opened by a single database user on an individual TiDB instance.

You can configure this limit globally or assign it directly to individual user accounts:

-- Set a cluster-wide default per instance
SET GLOBAL max_user_connections = 100;

-- Set a quota for a specific user
CREATE USER 'app_user'@'%' WITH MAX_USER_CONNECTIONS 100;

-- Modify an existing user's quota
ALTER USER 'app_user'@'%' WITH MAX_USER_CONNECTIONS 100;

Per-User Connection Quota Enforcement

Multi-tenant isolation using `max_user_connections` to prevent instance exhaustion

Client Traffic Ingress user: app_user (101) Breaching quota user: analytics (12) Within limit TiDB Instance (Port 4000) max_user_connections = 100 Instance quota per account app_user: 100/100 (Full) analytics: 12/100 (Normal) Evaluates active session count FATAL: ERROR 1040 Too many connections Handshake OK Session Allocated
Note: The default value is 0, which allows unlimited connections.

Verification with Sysbench

When a user reaches their configured threshold, TiDB rejects subsequent handshakes immediately with a standard MySQL error code:

root@mydbops-321:~$ sysbench oltp_read_only   --db-driver=mysql   --mysql-host=10.0.2.15   --mysql-port=4000   --mysql-user=mydbops_admin  --mysql-password='XXXXXXXX'   --mysql-db=test   --tables=1   --table-size=1000   --threads=100   --time=300   --report-interval=10   run
sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3)

Running the test with following options:
Number of threads: 100
Report intermediate results every 10 second(s)
Initializing random number generator from current time


Initializing worker threads...

FATAL: unable to connect to MySQL server on host '10.0.2.15', port 4000, aborting...
FATAL: error 1040: Too many connections
FATAL: `thread_init' function failed: /usr/share/sysbench/oltp_common.lua:349: connection creation failed
FATAL: unable to connect to MySQL server on host '10.0.2.15', port 4000, aborting...
FATAL: error 1040: Too many connections
FATAL: `thread_init' function failed: /usr/share/sysbench/oltp_common.lua:349: connection creation failed
FATAL: unable to connect to MySQL server on host '10.0.2.15', port 4000, aborting...
FATAL: error 1040: Too many connections
FATAL: `thread_init' function failed: /usr/share/sysbench/oltp_common.lua:349: connection creation failed
FATAL: unable to connect to MySQL server on host '10.0.2.15', port 4000, aborting...
FATAL: unable to connect to MySQL server on host '10.0.2.15', port 4000, aborting...
FATAL: error 1040: Too many connections
FATAL: `thread_init' function failed: /usr/share/sysbench/oltp_common.lua:349: connection creation failed
FATAL: unable to connect to MySQL server on host '10.0.2.15', port 4000, aborting...
FATAL: error 1040: Too many connections
FATAL: `thread_init' function failed: /usr/share/sysbench/oltp_common.lua:349: connection creation failed
FATAL: Thread initialization failed!
root@mydbops-321:~$ 
Note : The default value is 0

This provides an additional layer of protection against a single application or user consuming excessive connection resources and is useful in multi-tenant environments.

4. Improvements for ORDER BY ... LIMIT Queries

TiDB 8.5.7 improves how the query optimizer handles ORDER BY ... LIMIT statements when paired with OR or IN clauses on indexed columns.

Previously, using IndexMerge across multiple index paths meant scanning and pulling rows from each index branch into an intermediate buffer before performing a sort and applying the final LIMIT:

Index path 1 ──> many rows ──┐
                             ├──> Merge ──> Sort ──> LIMIT 20
Index path 2 ──> many rows ──┘

In 8.5.7, the optimizer pushes the TopN / LIMIT constraint directly into each individual index path before the merge occurs:

Index path 1 ──> TopN/LIMIT ──┐
                              ├──> Merge
Index path 2 ──> TopN/LIMIT ──┘

That means fewer rows need to travel through the rest of the execution plan.

Optimizer Execution Shift: TopN Push-Down

Eliminating wide intermediate row-sets before the merge stage

PRIOR TO TiDB 8.5.7 (UNCONSTRAINED MERGE) Index Path 1 Index Path 2 5,000 rows 5,000 rows Full Merge & Memory Sort LIMIT 20 High memory pressure & sorting latency TiDB 8.5.7 (PUSHED-DOWN TopN) Index Path 1 ↳ TopN Push-Down (20) Index Path 2 ↳ TopN Push-Down (20) 20 rows 20 rows Merge 40 Candidates 20 ✓ Minimum Coprocessor Row Scan & Network Transfer

Additionally, TiDB 8.5.7 introduces partial ordered index evaluation for TopN via the system variable tidb_opt_partial_ordered_index_for_topn. Setting this variable to COST allows the optimizer to leverage partial orderings when cost evaluations prove beneficial (default is DISABLE).

5. New System Variables in TiDB 8.5.7

Here is a summary of the new system variables introduced in this release:

New System Variables in TiDB 8.5.7

Filter or search the configuration variables added in this LTS maintenance release

Variable Name Default Allowed Values Scope Persistent Description
max_user_connections 0 0–100000 GLOBAL Yes Limits concurrent client connections established per user to a TiDB node.
performance_schema_session_connect_attrs_size 4096 -1–65536 bytes GLOBAL Yes Caps memory assigned to record connection attributes in Performance Schema.
tidb_enable_batch_query_region OFF ON / OFF GLOBAL Yes Enables batching of Region metadata requests transmitted to PD.
tidb_enable_cache_prepare_stmt OFF ON / OFF SESSION / GLOBAL Yes Caches execution structures for repeated prepare statements.
tidb_enable_strict_not_null_check ON ON / OFF SESSION / GLOBAL Yes Strictly blocks inserting explicit NULL into NOT NULL columns.
tidb_opt_enable_alternative_logical_plans OFF ON / OFF SESSION / GLOBAL Yes Allows evaluation of an alternative non-decorrelated logical execution plan.
tidb_opt_partial_ordered_index_for_topn DISABLE DISABLE / COST SESSION / GLOBAL Yes Activates cost-based partial-order optimization for TopN queries.

6. Operational Improvements

Graceful Shutdown Signaling via COM_PING

During rolling restarts or planned maintenance, proxies like HAProxy or AWS ALB rely on health checks to remove backend servers from their rotation pools.

In TiDB 8.5.7, shutting-down nodes now immediately return an error in response to COM_PING commands. This allows upstream proxies to detect the draining state right away, preventing new client traffic from hitting an instance that is terminating active sessions.

PD Maintenance Task Coordination

Automating infrastructure tasks (such as rolling OS updates or node-level disk replacements) requires extra caution in distributed databases so that multiple nodes within the same Raft group do not restart at the same time.

TiDB 8.5.7 adds dedicated maintenance endpoints to PD and matching pd-ctl commands. These APIs serialize TiKV maintenance requests across stores, ensuring automated maintenance tools do not accidentally compromise Raft quorum.

TiDB Lightning Web UI Deprecation

The standalone web interface for TiDB Lightning has been removed in 8.5.7. Large-scale data ingestion and checkpoint handling are now handled exclusively through the official command-line tools:

  • tidb-lightning: For data loading and pipeline management.
  • tidb-lightning-ctl: For checkpoint tracking, error resolution, and manual recovery.

Summary

The TiDB 8.5.7 maintenance release brings targeted improvements to both query execution and daily administration. Between partial index support for reducing storage overhead, CPU-driven Region scheduling for smoother load distribution, and max_user_connections for multi-tenant isolation, this update offers solid operational benefits for teams running high-throughput production clusters. If you are already running TiDB 8.5 LTS, planning an upgrade to 8.5.7 provides useful performance gains without requiring architectural changes.

Planning your TiDB 8.5.7 upgrade?

Mydbops TiDB experts can help you test new features like partial indexes and per-user connection limits, plan a safe rolling upgrade, and keep your cluster running smoothly in production.

No items found.

About the Author

Founding Partner at Mydbops. He leads the team’s TiDB work, including a 7 TB move from MySQL to TiDB with zero downtime. PingCAP has named him a TiDB Community Champion twice, and he was an AWS Community Builder from 2021 to 2024.

Subscribe Now!

Subscribe here to get exclusive updates on upcoming webinars, meetups, and to receive instant updates on new database technologies.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.