What’s New in TiDB 8.5.7: Partial Indexes, CPU Scheduling & Connection Limits
.avif)
.avif)
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)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.
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;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.
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:
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.


_%20How%20MySQL%2026.7%20Fixes%20Replication%20Lag%20(1).avif)
_%20How%20MySQL%2026.7%20Fixes%20Replication%20Lag.avif)
.avif)
