왜 지금 봐야 하나
TiDB 8.5.7은 2026-07-09에 나온 패치 릴리스다. 그런데 이번 릴리스는 일반적인 버그 수정판으로 보기 어렵다. 읽기 hotspot을 어떤 신호로 옮길지, 어떤 행만 인덱싱할지, 누가 연결 슬롯을 얼마나 가져갈지라는 세 가지 운영 경계를 함께 바꿨기 때문이다.
기존 TiDB 운영에서 자주 보이는 낭비는 대체로 세 종류다.
- QPS와 byte throughput은 비슷한데 특정 TiKV만 CPU가 먼저 포화되는 읽기 hotspot
- 전체 테이블에 인덱스를 유지하느라 쓰기 비용과 인덱스 저장 공간이 같이 커지는 희소 조건 인덱싱
- 특정 사용자나 서비스 계정이 연결을 과도하게 열어 한 TiDB 인스턴스의 응답성을 떨어뜨리는 연결 폭주
TiDB 8.5.7은 이 세 문제에 각각 CPU-aware hot Region scheduling, partial index, max_user_connections를 내놨다. 서로 다른 기능처럼 보이지만, 운영자 관점에서는 모두 “보이지 않던 낭비를 제어면으로 끌어올린 것”이라는 공통점이 있다.
TiDB 8.5.7에서 새로 열린 제어면
1. CPU-aware hot Region scheduling: QPS가 아니라 CPU로 읽기 hotspot을 옮긴다
TiDB의 기존 hot Region scheduler는 읽기 hotspot을 주로 query rate와 byte throughput으로 판단했다. 이 방식은 네트워크와 요청 수만 비슷하면 균형처럼 보인다. 하지만 실제 운영에서는 다음 같은 경우가 자주 생긴다.
- 동일한 QPS라도 쿼리별 CPU 비용이 크게 다른 경우
- TiKV 노드별 하드웨어 프로파일이나 co-located workload가 다른 경우
- MVCC 버전 스캔 때문에 byte는 비슷해도 CPU만 먼저 타는 경우
이때는 “트래픽은 균형인데 CPU는 한쪽만 먼저 죽는” 이상한 상태가 나온다. 8.5.7의 변화는 이 지점을 정확히 겨냥한다. TiKV가 hot Region의 read CPU 사용량을 store heartbeat로 보고하고, PD가 그 값을 read hotspot scheduling의 우선순위로 사용할 수 있게 됐다.
실제로 확인해야 할 제어면은 두 곳이다.
pd-ctl hot read/hot history결과에flow_cpu가 보이는지pd-ctl hot store와scheduler config balance-hot-region-scheduler에서 CPU 기반 설정이 보이는지
tiup ctl:v8.5.7 pd -u http://<pd-host>:2379 hot store
tiup ctl:v8.5.7 pd -u http://<pd-host>:2379 scheduler config balance-hot-region-scheduler문서 기준으로, CPU 보고가 가능한 클러스터에서는 balance-hot-region-scheduler의 기본 read-priorities가 cpu,byte로 잡힌다. CPU 보고가 되지 않으면 PD는 자동으로 query,byte, 더 좁게는 byte,key로 되돌아간다. 즉, 업그레이드만 했다고 항상 CPU-aware scheduling이 켜지는 것이 아니라, TiKV가 CPU 신호를 정상 보고하는지까지 확인해야 한다.
운영자가 같이 봐야 하는 설정은 다음 두 개다.
min-hot-cpu-rate: hotspot 통계에 들어갈 최소 CPU 사용량. 기본값은 한 CPU 코어의 10%cpu-rate-rank-step-ratio: CPU 기준으로 store 랭킹 차이를 얼마나 민감하게 볼지 정하는 step 비율. 기본값은0.05
이 기능의 의미는 “load-based replica read가 필요 없어졌다”가 아니다. load-based replica read는 읽기 요청을 다른 replica로 우회시키는 질의 경로 제어이고, CPU-aware hot Region scheduling은 Region 자체를 어느 TiKV에 둘지 바꾸는 배치 제어다. 둘은 같은 층위의 기능이 아니다.
정리하면 8.5.7의 이 기능은 read hotspot을 QPS/throughput 문제에서 CPU 편향 문제까지 확장해 다룬다는 점이 핵심이다.
2. Partial index: 전체 테이블이 아니라 필요한 행만 인덱싱한다
8.5.7의 SQL 레이어 변화 중 가장 실무적인 것은 partial index다. TiDB는 이제 CREATE INDEX ... WHERE ... 또는 ALTER TABLE ... ADD INDEX ... WHERE ... 형태로, 조건을 만족하는 행에만 보조 인덱스를 생성할 수 있다.
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
status VARCHAR(20),
created_at DATETIME,
deleted_at DATETIME
);
CREATE INDEX idx_queued_orders_created_at
ON orders (created_at)
WHERE status = 'queued';이 기능이 필요한 상황은 분명하다.
- 전체 행 중 일부 상태(
pending,active,queued)만 자주 조회하는 워크로드 deleted_at IS NULL같은 soft-delete 패턴- 특정 조건에서만 유니크 제약이 필요한 경우
- 전체 인덱스를 유지하기엔 DML 부담이 큰 대형 테이블
TiDB 문서가 강조하는 중요한 조건은 하나다. 옵티마이저는 질의 조건이 partial index의 predicate와 일치하거나, 그 predicate를 함의할 때만 이 인덱스를 선택한다.
즉, 아래처럼 쿼리 필터와 인덱스 predicate를 의도적으로 맞춰 설계해야 한다.
- 인덱스:
WHERE status = 'active' - 잘 맞는 쿼리:
WHERE status = 'active' AND name = 'John' - 잘 안 맞는 쿼리:
WHERE status IN ('active', 'pending')
현재 8.5.7에서 predicate가 지원하는 범위도 넓지 않다. 공식 release notes 기준으로 partial index predicate는 다음 정도만 안정적으로 지원한다.
- 기본 비교 연산자:
=,!=,<,<=,>,>= IS NULL,IS NOT NULL- 상수값을 쓰는
IN (...)
이 제한은 오히려 운영자에게 명확한 메시지를 준다. 이번 릴리스의 partial index는 범용 논리식 엔진이 아니라, 희소 조건을 빠르게 잘라내는 1차 도구에 가깝다. 즉, soft-delete, 상태 플래그, 큰 점수 구간, 비어 있지 않은 큐 같은 패턴에서 먼저 써보는 편이 맞다.
실제 도입 판단은 세 가지를 같이 봐야 한다.
- 인덱스 크기 감소: 전체 secondary index 대비 얼마나 줄었는지
- 쓰기 비용 감소: predicate 밖의 행이 많은 만큼
INSERT/UPDATE/DELETE부담이 실제로 내려가는지 - 플랜 선택 안정성:
EXPLAIN에서 partial index가 예상한 쿼리에 꾸준히 선택되는지
EXPLAIN SELECT *
FROM orders
WHERE status = 'queued'
ORDER BY created_at;운영적으로는 “partial index를 만들었으니 무조건 빨라진다”보다 predicate가 정말 희소한가, 질의가 그 predicate를 일관되게 따르는가가 더 중요하다. 조건이 넓거나 애플리케이션 필터가 자주 흔들리면, 이 기능은 기대만큼의 저장 공간 절감이나 플랜 안정성을 주지 못한다.
3. max_user_connections: noisy tenant를 인스턴스 경계에서 자른다
세 번째 변화는 화려하지 않지만 공유 클러스터 운영에서는 매우 현실적이다. 8.5.7부터 TiDB는 한 사용자가 TiDB 인스턴스 하나에 맺을 수 있는 최대 연결 수를 max_user_connections로 제한할 수 있다.
공식 문서 기준 핵심 속성은 다음과 같다.
- Scope:
GLOBAL - Persists to cluster:
Yes - Range:
0~100000 - 기본값
0: 제한 없음 - 실제 상한은
max_user_connections와max_connections중 더 작은 값
SET GLOBAL max_user_connections = 200;
CREATE USER 'etl_worker'@'%' IDENTIFIED BY '***'
WITH MAX_USER_CONNECTIONS 30;
ALTER USER 'readonly_api'@'%'
WITH MAX_USER_CONNECTIONS 20;중요한 경계는 이 제한이 클러스터 전체가 아니라 “TiDB server instance 하나” 기준이라는 점이다. TiDB 인스턴스가 4대라면, 로드밸런서 분산 상태에 따라 같은 계정이 총 120개 연결을 가질 수도 있다. 따라서 이 기능은 “전사 공통 사용자 총량 규제”보다 다음 상황에 더 맞는다.
- 특정 배치 계정이 한 인스턴스의 연결 슬롯을 과도하게 점유하는 문제
- 멀티테넌트 SaaS에서 일부 tenant의 connection burst가 다른 tenant 응답성에 영향을 주는 문제
- connection pool misconfiguration으로 특정 서비스만 연결을 무한 증식시키는 문제
TiDB release notes는 이 기능이 한 사용자의 과도한 token consumption이 다른 요청의 응답을 밀어내는 상황을 막는 데 유용하다고 설명한다. 운영자 입장에서는 “쿼리 느림”이 아니라 입구에서 세션 폭주를 자르는 단순한 가드레일이 추가된 셈이다.
4. 세 기능을 같이 보면: 8.5.7은 "낭비가 새는 위치"를 더 잘 고른다
이번 릴리스의 세 기능을 따로 보면 산만해 보일 수 있다. 하지만 운영면에서 묶어 보면 분명한 그림이 나온다.
| 제어 대상 | 이전 판단 기준 | 8.5.7 이후 | 운영자가 얻는 것 |
|---|---|---|---|
| 읽기 hotspot | query rate, byte throughput 위주 | CPU까지 포함한 Region 재배치 | CPU만 먼저 죽는 불균형을 더 빨리 교정 |
| 보조 인덱스 | 전체 행 대상 full index | predicate를 만족하는 행만 index | 인덱스 저장 공간과 DML 유지 비용 절감 |
| 연결 admission | 인스턴스 전체 max_connections 위주 | 사용자별 per-instance cap | noisy user를 입구에서 차단 |
즉, 8.5.7은 성능을 마술처럼 올리는 릴리스가 아니다. 대신 CPU, 인덱스, 연결 수라는 서로 다른 낭비를 각각 더 가까운 경계에서 자르게 하는 릴리스다. 이런 변화는 화려한 벤치마크보다 운영 런북을 더 많이 바꾼다.
도입 순서: 한 번에 다 켜지 말고, 세 층을 따로 검증한다
1) CPU-aware scheduling
- 업그레이드 후
pd-ctl hot store,hot read/history에cpu-read-rate,flow_cpu가 보이는지 확인 scheduler config balance-hot-region-scheduler에서read-priorities가cpu,byte인지 확인- read hotspot이 심한 서비스 하나를 골라, 전후 p95/p99와 TiKV CPU 편차를 비교
2) Partial index
status,deleted_at,queue_state,tenant_enabled처럼 희소도가 높은 predicate 한 개만 먼저 고른다CREATE INDEX ... WHERE ...로 만들고, 반드시EXPLAIN으로 실제 사용 여부 확인- 인덱스 크기와 해당 테이블의 DML latency를 전후 비교
3) max_user_connections
- ETL, backfill, ad-hoc analyst, admin 계정을 분리한 뒤 noisy 계정부터 낮은 값으로 시작
max_connections보다 큰 값을 줘도 의미 없으니, 인스턴스 전체 상한과 함께 설계- 여러 TiDB 인스턴스 뒤에 로드밸런서를 두었다면, per-instance 합산 효과를 운영 문서에 명시
어떤 팀이 먼저 써볼 만한가
다음과 같은 팀이라면 8.5.7의 가치가 바로 보인다.
- 읽기 hotspot이 QPS가 아니라 CPU skew로 나타나는 팀
- soft-delete나 상태 플래그가 많은 OLTP 서비스
- 멀티테넌트 환경에서 일부 계정의 connection burst가 반복되는 팀
- TiDB를 단순 MySQL 대체가 아니라 분산 OLTP control plane으로 다루는 팀
반대로, 작은 단일 서비스에서 테이블도 작고 TiDB 인스턴스 수가 적다면 이번 릴리스의 체감은 제한적일 수 있다. 이 경우 partial index 하나만 먼저 써보는 편이 보통 가장 비용이 낮다.
Open question
- 공식 문서는
read-priorities,min-hot-cpu-rate,cpu-rate-rank-step-ratio를 설명하지만, CPU와 byte가 충돌할 때 실제 스케줄링 결과가 워크로드별로 얼마나 달라지는지는 스테이징 canary로 확인하는 편이 안전하다. - partial index는
8.5.7에서 predicate 범위가 제한적이다. 애플리케이션 쿼리가 더 복잡한 논리식으로 흘러가면 옵티마이저가 partial index를 안정적으로 고르지 못할 수 있으므로, 쿼리 템플릿과 인덱스 predicate를 함께 관리해야 한다. max_user_connections는 per-instance 경계다. 프록시/로드밸런서 정책까지 같이 보지 않으면 “사용자별 총량 제한”으로 오해할 수 있다.
References
- PingCAP, "TiDB 8.5.7 Release Notes". https://docs.pingcap.com/tidb/stable/release-8.5.7
- PingCAP, "TiDB 8.5.0 Release Notes". https://docs.pingcap.com/tidb/stable/release-8.5.0
- PingCAP, "CREATE INDEX" (
Partial indexes, new in v8.5.7). https://docs.pingcap.com/tidb/v8.5/sql-statement-create-index#partial-indexes - PingCAP, "Troubleshoot Hotspot Issues" (
CPU-aware hot Region scheduling for read hotspots). https://docs.pingcap.com/tidb/v8.5/troubleshoot-hot-spot-issues#cpu-aware-hot-region-scheduling-for-read-hotspots - PingCAP, "PD Control User Guide" (
balance-hot-region-scheduler,cpu-read-rate,min-hot-cpu-rate,cpu-rate-rank-step-ratio). https://docs.pingcap.com/tidb/v8.5/pd-control#scheduler-config-balance-hot-region-scheduler - PingCAP, "System Variables" (
max_user_connections, new in v8.5.7). https://docs.pingcap.com/tidb/v8.5/system-variables#max_user_connections-new-in-v857