Manticore 实时删除索引数据的实践方案

2026-02-24 54 浏览 0 评论

在实际项目中,尤其是爬虫或 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)

既然不能物理删除,那就换一种思路:

通过字段标记的方式,实现“逻辑删除”

这也是搜索系统中非常常见、成熟的一种方案。

核心思路

  1. 在索引中增加一个 deleted 字段
  2. 默认值表示「未删除」
  3. 删除时只更新该字段
  4. 查询时过滤掉已删除数据

这样就可以做到:

  • 实时生效
  • 无需重建索引
  • 查询性能几乎不受影响

创建支持删除标记的索引

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 增量同步 ,这个删除标记字段依然可以无缝复用,是一个非常值得长期保留的设计。


发布评论

发布评论前请先 登录
取消
0 评论
点赞
收藏

评论列表 0

暂无评论