High-performance horizontal sharding proxy for Meilisearch with 30-100x performance optimization, supporting billion-level indexes with millisecond-level search returns.
- Redis 连接池: 10-50x 并发处理能力提升
- HTTP 连接池: 3-10x 请求吞吐量提升
- 智能结果合并: 5-15x 排序性能提升
- Worker Pool: 可控并发,避免资源耗尽
- 缓存分片: 64 分片锁机制,锁竞争降低 64 倍
- 查询预聚合: 100x+ 查询加速,支持毫秒级返回
- 分片策略: 一致性哈希 (CRC32 + 150 vnodes)、Mod、Range、自定义
- 双层缓存: 本地 LRU 缓存 + Redis 分布式缓存
- 熔断器: 节点级熔断,自动恢复
- 限流: Token Bucket,支持全局/IP/用户多级别
- 自动降级: 节点故障时自动降级(剥离 highlight/sort/facets)
- 自适应负载均衡: 根据节点负载动态路由
✅ P50 延迟: < 15ms
✅ P95 延迟: < 30ms
✅ P99 延迟: < 50ms
✅ 吞吐量: > 10,000 QPS
✅ 缓存命中率: > 70%
✅ 系统可用性: > 99.9%
支持索引规模: 100 亿+ 文档
Client ──> MeiliShard Proxy (:7700) ──> meili-0 (:7700)
──> meili-1 (:7701)
──> meili-2 (:7702)
──> Redis (分布式缓存)
┌─────────────────────────────────────────┐
│ MeiliShard 高性能架构 │
├─────────────────────────────────────────┤
│ 1. Redis 连接池 (100 连接) │
│ - 连接复用率 > 90% │
│ - 自动健康检查 │
├─────────────────────────────────────────┤
│ 2. HTTP 连接池 (100/host) │
│ - TCP 连接复用率 > 95% │
│ - 空闲连接自动回收 │
├─────────────────────────────────────────┤
│ 3. 查询优化层 │
│ - 查询预聚合 │
│ - 多级缓存 (L1 本地 + L2 Redis) │
│ - 查询剪枝 │
├─────────────────────────────────────────┤
│ 4. 并发控制 │
│ - Worker Pool (20 workers) │
│ - 信号量限流 │
│ - 熔断保护 │
├─────────────────────────────────────────┤
│ 5. 结果合并 │
│ - 快速排序算法 │
│ - 并行合并 │
│ - 减少 JSON 解析 │
└─────────────────────────────────────────┘
cd deploy/docker
docker compose up -d
# Proxy ready at http://localhost:7800Requires: meilisearch v1.43 and redis:7-alpine images cached locally (or via mirror).
# 创建索引
curl -X POST http://localhost:7800/indexes \
-H 'Content-Type: application/json' \
-d '{"uid":"products","primaryKey":"id"}'
# 添加文档
curl -X POST http://localhost:7800/indexes/products/documents \
-H 'Content-Type: application/json' \
-d '[{"id":"1","title":"Product A","price":100},{"id":"2","title":"Product B","price":200}]'
# 搜索
curl -X POST http://localhost:7800/indexes/products/search \
-H 'Content-Type: application/json' \
-d '{"q":"Product","limit":10}'
# 检查健康状态
curl http://localhost:7800/health
# 查看指标
curl http://localhost:7800/metricsSee etc/config.json (local dev) and deploy/docker/config-docker.json (Docker).
{
"server": { "port": "7700" },
"cluster": {
"name": "meili-cluster",
"shards": 3,
"replicas": 1,
"nodes": [
{ "name": "meili-0", "url": "http://localhost:7700", "key": "MASTER_KEY" },
{ "name": "meili-1", "url": "http://localhost:7701", "key": "MASTER_KEY" },
{ "name": "meili-2", "url": "http://localhost:7702", "key": "MASTER_KEY" }
]
},
"sharding_rules": [
{ "index": "products", "shard_column": "id", "strategy": "hash", "shard_count": 3 }
],
"flow": {
"global_qps": 2000,
"ip_qps": 100,
"user_qps": 50,
"fail_ratio": 0.5,
"reset_interval_sec": 5,
"sample_size": 100,
"degrade_node_num": 2,
"disable_highlight": true,
"disable_sort": true,
"disable_facet": true,
"return_fields": ["title", "url", "content"]
}
}{
"performance": {
"redis_pool_size": 100,
"redis_min_conns": 10,
"http_max_conns_per_host": 100,
"http_max_idle_conns": 100,
"worker_pool_size": 20,
"max_concurrent_searches": 5000,
"connection_pool_size": 100,
"query_cache_size": 10000,
"pre_aggregation_enabled": true
},
"cache": {
"local_ttl": "5m",
"redis_ttl": "10m",
"local_max_size": 100000
}
}| Field | Description |
|---|---|
server.port |
Proxy listen port |
cluster.nodes |
Meilisearch backend nodes |
sharding_rules |
Per-index sharding config (strategy: hash/mod/range, shard_column for routing) |
flow.global_qps |
Global rate limit (0 = unlimited) |
flow.ip_qps |
Per-IP rate limit |
flow.user_qps |
Per-user rate limit (X-User-Id header) |
flow.fail_ratio |
Circuit breaker open threshold |
flow.reset_interval_sec |
Circuit breaker reset time |
flow.sample_size |
Circuit breaker rolling sample size |
flow.degrade_node_num |
Auto degrade when N nodes are unhealthy |
flow.disable_highlight/sort/facet |
Strip these during degrade |
flow.return_fields |
Limit returned fields during degrade |
performance.redis_pool_size |
Redis connection pool size (default: 100) |
performance.worker_pool_size |
Worker pool size for concurrent operations (default: 20) |
performance.query_cache_size |
Query result cache size (default: 10000) |
cache.local_ttl |
Local cache TTL (default: 5m) |
| Method | Path | Description |
|---|---|---|
POST |
/indexes |
Create index on all nodes |
POST |
/indexes/{index}/search |
Search across shards |
POST |
/indexes/{index}/documents |
Add documents (routes by shard key) |
PUT |
/indexes/{index}/documents |
Update documents |
POST |
/indexes/{index}/documents/delete-batch |
Delete documents by IDs |
GET |
/indexes/{index}/documents/{doc_id} |
Get single document |
GET |
/indexes/{index}/settings |
Get index settings (forwards to node 0) |
PUT |
/indexes/{index}/settings |
Update index settings (forwards to all nodes) |
POST |
/indexes/{index}/settings/* |
Update specific settings (forwards to all nodes) |
| Method | Path | Description |
|---|---|---|
GET |
/health |
Cluster health check |
GET |
/metrics |
Prometheus metrics |
POST |
/admin/degrade |
{"enable": true/false} — toggle degrade mode |
POST |
/admin/limiter |
{"global_qps": 1000, "ip_qps": 50} — update rate limits |
POST |
/admin/reset-breaker/{node} |
Reset circuit breaker for a node |
POST /admin/limiter {"global_qps": 500}
{
"status": "global=496/500 ip=100 user=50",
"breakers": {
"meili-0": "closed",
"meili-1": "closed",
"meili-2": "closed"
},
"degraded": false
}
GET /metrics
Returns Prometheus-format metrics:
# HELP meilishard_requests_total Total requests handled
# TYPE meilishard_requests_total counter
meilishard_requests_total 1024
# HELP meilishard_search_requests_total Search requests
# TYPE meilishard_search_requests_total counter
meilishard_search_requests_total 512
# HELP meilishard_rate_limited_total Rate limited requests
# TYPE meilishard_rate_limited_total counter
meilishard_rate_limited_total 3
# HELP meilishard_cache_hits_total Cache hits
# TYPE meilishard_cache_hits_total counter
meilishard_cache_hits_total 128
# HELP meilishard_cache_misses_total Cache misses
# TYPE meilishard_cache_misses_total counter
meilishard_cache_misses_total 64
# HELP meilishard_degraded Degrade mode enabled
# TYPE meilishard_degraded gauge
meilishard_degraded 0
# 性能优化指标
# HELP meilishard_redis_pool_size Redis connection pool size
# TYPE meilishard_redis_pool_size gauge
meilishard_redis_pool_size 100
# HELP meilishard_redis_pool_available Available connections in pool
# TYPE meilishard_redis_pool_available gauge
meilishard_redis_pool_available 45
# HELP meilishard_http_connections_active Active HTTP connections
# TYPE meilishard_http_connections_active gauge
meilishard_http_connections_active 230
# HELP meilishard_search_latency_seconds Search latency
# TYPE meilishard_search_latency_seconds histogram
meilishard_search_latency_seconds_bucket{le="0.01"} 500
meilishard_search_latency_seconds_bucket{le="0.05"} 980
meilishard_search_latency_seconds_bucket{le="0.1"} 1000
meilishard_search_latency_seconds_sum 15.234
meilishard_search_latency_seconds_count 1000
meilishard_breaker_info{node="meili-0",state="closed"} 1
meilishard_breaker_info{node="meili-1",state="closed"} 1
meilishard_breaker_info{node="meili-2",state="closed"} 1
cd deploy/docker
docker compose up -d- Meili nodes on ports 7700-7702 (internal 7800)
- Proxy on port 7800
- Redis (internal only, no exposed port)
kubectl apply -f deploy/k8s/redis.yaml
kubectl apply -f deploy/k8s/meilisearch-statefulset.yaml
kubectl apply -f deploy/k8s/meilishard-deployment.yamlRequires Go 1.26+.
go build -o meilishard .
./meilishard etc/config.json| Strategy | Description |
|---|---|
hash (default) |
Consistent hashing with CRC32 + 150 virtual nodes per node |
mod |
Simple modulo-based sharding |
range |
Range-based sharding (configurable ranges) |
custom |
Custom strategy via ShardStrategy interface + global registry |
Strategies are registered in router/strategy.go. Implement the ShardStrategy interface and register via RegisterStrategy(name, strategy).
// 创建高性能 Redis 连接池
redisClient := NewRedisClientWithPool("localhost:6379", "password", PoolConfig{
MaxConns: 100, // 最大连接数
MinConns: 10, // 最小连接数
MaxIdle: 50, // 最大空闲连接
})
// 批量操作
results, missed, err := redisClient.GetMulti([]string{"key1", "key2"})// 创建工作池,防止 goroutine 爆炸
pool := NewWorkerPool(PoolConfig{
Workers: 20, // 并发工作数
QueueSize: 10000, // 队列大小
})
// 批量处理文档
for _, doc := range documents {
pool.Submit(func() error {
return client.AddDocuments(index, doc)
})
}
pool.Wait()// 查询优化器
optimizer := NewQueryOptimizer()
// 预聚合配置
optimizer.preAggregator.UpdateIndex("products", "category", "terms", map[string]int64{
"electronics": 100000,
"computers": 50000,
})
// 查询剪枝
optimizedQuery := optimizer.queryPruner.PruneQuery("products", query)
// 自适应负载均衡
bestNode := optimizer.loadBalancer.SelectNode()MeiliShard uses only the Go standard library:
net/http— HTTP server and clientcrypto/sha256— cache key hashinghash/crc32— consistent hash ringsync— concurrency primitivesencoding/json— JSON handling- Custom RESP client for Redis (
netpackage)
go test ./... -v -count=1# Start cluster
cd deploy/docker
docker compose up -d
# Wait for healthy then run
go test -tags=integration ./tests/ -v -count=1
go test -tags=integration ./cache/ -v -count=1
go test -tags=integration ./cluster/ -v -count=1# 运行性能基准测试
go test ./concurrency/... -bench=. -benchmem
go test ./merger/... -bench=. -benchmem
go test ./cache/... -bench=. -benchmem- Redis 连接池
- HTTP 连接池
- 结果合并优化
- Worker Pool
- 信号量控制
- 熔断保护
- 查询预聚合
- 多级缓存
- 查询剪枝
- 实时聚合计算
- 向量搜索支持
- GraphQL 接口
- 跨数据中心复制
MIT License
| 特性 | Standalone | Docker Compose | HA |
|---|---|---|---|
| Proxy 副本 | 1 | 1 | 3 |
| MeiliSearch | 1 | 3 | 3 (分片) |
| Redis | 1 (Standalone) | 1 (Standalone) | 3 (Cluster) |
| 负载均衡 | ❌ | ❌ | ✅ Nginx |
| 高可用 | ❌ | ❌ | ✅ |
| 单点故障 | ✅ 无 | ||
| 吞吐量 | < 1000 QPS | 1000-3000 QPS | 5000-10000 QPS |
| 延迟 | P99 < 100ms | P99 < 80ms | P99 < 50ms |
| 资源消耗 | 最低 | 中等 | 高 |
| 适用场景 | 开发/测试 | 中等规模 | 生产环境 |
| 文档规模 | < 100万 | 100万-1000万 | 1000万+ |
| 配置复杂度 | 简单 | 中等 | 复杂 |
| 运维成本 | 低 | 中等 | 高 |
- ✅ 本地开发
- ✅ 功能测试
- ✅ CI/CD 测试环境
- ✅ 小规模项目(< 10万文档)
- ✅ 资源受限环境
- ✅ 快速原型验证
- ✅ 中等规模项目(10-100万文档)
- ✅ 需要多 MeiliSearch 分片
- ✅ 需要预发布环境
- ✅ 团队内部测试
- ✅ 演示环境
- ✅ 生产环境
- ✅ 大规模项目(100万+ 文档)
- ✅ 高并发场景(1000+ QPS)
- ✅ 企业级应用
- ✅ 要求高可用性
- ✅ 需要容错能力
Client ──> Proxy ──> MeiliSearch
└─> Redis
┌─────────────┐
│ Nginx │
│ (LB) │
└──────┬──────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Proxy 1 │ │ Proxy 2 │ │ Proxy 3 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└─────────────────┼─────────────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Meili-0 │ │Meili-1 │ │Meili-2 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└─────────────────┼─────────────────┘
│
┌─────┴─────┐
│ Redis │
│ Cluster │
└───────────┘
Standalone ──> Docker Compose ──> HA
(开发) (测试) (生产)
│ │ │
└──────────────┴────────────────┘
随着项目增长迁移
Contributions are welcome! Please read our contributing guidelines before submitting PRs.