Manticore 实时删除索引数据的实践方案
在实际项目中,尤其是爬虫或 P2P 相关系统里, 种子数据的增长速度非常快 。数据一多,就不可避免地会混入一些垃圾数据或违规内容。原始数据存储在 MySQL 的 torrent 表中,这部分数据删除并不复杂;但问题在于—— Manticore 中的索引数据仍然存在 。
虽然在下次 重建索引(reindex) 时这些数据会被同步移除,但当索引数据量达到千万级甚至更高时, 全量重建索引的成本非常高 :
- 重建时间长
- CPU / IO 压力大
- 对线上搜索服务有影响
因此,一个很自然的需求就是: 👉 能不能实时删除 Manticore 索引里的某一条数据?
直接 DELETE 的尝试与失败
最开始,我尝试直接通过 MySQL 协议对 Manticore 的索引表执行 DELETE :
delete from p2pspider where id = 8509432结果直接报错:
2026-02-06 22:59:52,281 ERROR 12961 [-/171.222.182.93/-/60ms POST /api/v1/deleteTorrent] nodejs.ER_PARSE_ERRORError: table p2pspider: table not available, or does not support DELETE
...
message: "table p2pspider: table not available, or does not support DELETE"
sql: "delete from p2pspider where id = 8509432"这个错误其实非常关键,它明确告诉我们:
Manticore 的普通 RT / disk index 并不支持 DELETE 语句
为什么不能 DELETE?
这是由 Manticore(以及早期 Sphinx)索引的底层设计决定的:
- 索引文件是高度压缩的倒排结构
- 文档 ID 与词项之间存在复杂映射
- 物理删除一条记录意味着大量索引重写
所以官方并没有为普通索引提供 DELETE FROM index 的能力。
可行方案:标记删除(Soft Delete)
既然不能物理删除,那就换一种思路:
通过字段标记的方式,实现“逻辑删除”
这也是搜索系统中非常常见、成熟的一种方案。
核心思路
- 在索引中增加一个
deleted字段 - 默认值表示「未删除」
- 删除时只更新该字段
- 查询时过滤掉已删除数据
这样就可以做到:
- 实时生效
- 无需重建索引
- 查询性能几乎不受影响
创建支持删除标记的索引
在 source 中,我们人为构造一个 deleted 字段,默认值设为 2 (表示未删除):
source p2pspider
{
type = mysql
sql_host = 127.0.0.1
sql_user = root
sql_pass = knxiSoH112
sql_db = p2pspider
sql_port = 3306
sql_query_pre = SET NAMES utf8mb4
sql_query = SELECT
id,
name title,
files content,
length,
file_count,
download_count,
type,
2 deleted
FROM torrent
# 全文搜索字段
sql_field_string = title
sql_field_string = content
# 排序 / 筛选字段
sql_attr_bigint = length
sql_attr_uint = file_count
sql_attr_uint = download_count
sql_attr_uint = type
# 删除标志
sql_attr_uint = deleted
}这里有几个细节值得注意:
2 deleted是一个 常量字段deleted必须声明为sql_attr_uint- 不要把删除标志做成全文字段,否则毫无意义
重新构建索引
修改配置后,需要重新生成索引:
indexer --rotate p2pspider--rotate 可以在不影响线上服务的情况下平滑替换索引,是生产环境必备选项。
查询时过滤已删除数据
查询时只需要在 WHERE 条件中增加一行过滤即可:
let sql = `
select id, title
from p2pspider
where MATCH('${params.word}')
and deleted = 2
`;这样一来:
- 被标记为
deleted = 1的数据 - 在搜索结果中将 完全不可见
实时删除的实现方式
删除时,不再使用 DELETE ,而是更新字段:
const connection = await this.getConnect();
const sql = `update p2pspider set deleted = 1 where id = ${id}`;
const val = await connection.query(sql);这一操作的优势在于:
- 实时生效
- 不需要重建索引
- 操作成本极低
这种方案的优缺点
优点
- 实现简单
- 性能稳定
- 适合大规模数据
- 生产环境验证充分
缺点
- 索引文件体积不会立即变小
- 被删除的数据仍然存在于索引中
不过这并不是问题,可以通过:
- 定期全量重建索引
- 离线清理长期 deleted 数据
来解决。
总结
在 Manticore 中:
- ❌ 不支持直接 DELETE 索引数据
- ✅ 推荐使用标记删除(Soft Delete)
- ✅ 通过属性字段 + 查询过滤实现实时删除效果
这种方式在搜索引擎领域是非常经典、可靠的实践,既满足了实时性,又避免了频繁重建索引带来的性能问题。
如果你后面还打算结合 RT index / 分布式索引 / Binlog 增量同步 ,这个删除标记字段依然可以无缝复用,是一个非常值得长期保留的设计。




