不行。 radosgw-admin bucket rm --purge-objects 对 indexless bucket 会"成功"退出(exit 0),但 所有对象数据都不会被删除,而是成为孤儿对象(orphan)永久残留在数据池里。
1. indexless 的本质:索引更新全部变成 no-op
indexless 只是把 RGWBucketInfo.index_type 置为 RGWBIType_Indexless(src/rgw/rgw_common.h:1458),而索引的写入路径全部被"盲化"(blind),即不再真正写索引:
cpp
// src/rgw/rgw_rados.h:1809-1812
UpdateIndex(...) : target(_target), obj(_obj),
bs(target->get_store()), csp_op_category(_c) {
blind = (target->get_bucket_info().index_type == RGWBIType_Indexless);
}
cpp
// src/rgw/rgw_rados.cc:6740 / 6774 / 6827
int RGWRados::Bucket::UpdateIndex::prepare(...) { if (blind) return 0; ... } // 不写索引
int RGWRados::Bucket::UpdateIndex::complete(...) { if (blind) return 0; ... } // 不写索引
int RGWRados::Bucket::UpdateIndex::remove(...) { if (blind) return 0; ... } // 不写索引
即:从写入那一刻起,索引里就从来没有对象条目 (索引对象 .dir.<bucket_id> 存在但为空)。
2. bucket rm --purge-objects 只靠索引找对象
调用链:rgw_admin.cc:7101 → RGWBucket::remove(rgw_bucket.cc:1091)→ rgw_remove_bucket(rgw_bucket.cc:549)。对象发现的唯一途径是索引列举:
cpp
// src/rgw/rgw_bucket.cc:577-596
do {
objs.clear();
ret = list_op.list_objects(max, &objs, &common_prefixes, &is_truncated); // ← 只读索引
...
for (const auto& obj : objs) {
ret = rgw_remove_object(store, info, bucket, key); // 删除对象
}
} while(is_truncated);
indexless bucket 的索引恒为空 → objs 恒为空 → 一个对象都不会被删 ,但命令仍走到 delete_bucket(rgw_rados.cc:5237),把 bucket 元数据 + 空的索引对象删掉,正常返回 0。
#mermaid-svg-4R4g7ik1ayQ7B0Dt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .error-icon{fill:#552222;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .marker.cross{stroke:#333333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4R4g7ik1ayQ7B0Dt p{margin:0;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .cluster-label text{fill:#333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .cluster-label span{color:#333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .cluster-label span p{background-color:transparent;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .label text,#mermaid-svg-4R4g7ik1ayQ7B0Dt span{fill:#333;color:#333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .node rect,#mermaid-svg-4R4g7ik1ayQ7B0Dt .node circle,#mermaid-svg-4R4g7ik1ayQ7B0Dt .node ellipse,#mermaid-svg-4R4g7ik1ayQ7B0Dt .node polygon,#mermaid-svg-4R4g7ik1ayQ7B0Dt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .rough-node .label text,#mermaid-svg-4R4g7ik1ayQ7B0Dt .node .label text,#mermaid-svg-4R4g7ik1ayQ7B0Dt .image-shape .label,#mermaid-svg-4R4g7ik1ayQ7B0Dt .icon-shape .label{text-anchor:middle;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .rough-node .label,#mermaid-svg-4R4g7ik1ayQ7B0Dt .node .label,#mermaid-svg-4R4g7ik1ayQ7B0Dt .image-shape .label,#mermaid-svg-4R4g7ik1ayQ7B0Dt .icon-shape .label{text-align:center;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .node.clickable{cursor:pointer;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .arrowheadPath{fill:#333333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4R4g7ik1ayQ7B0Dt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4R4g7ik1ayQ7B0Dt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4R4g7ik1ayQ7B0Dt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .cluster text{fill:#333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .cluster span{color:#333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4R4g7ik1ayQ7B0Dt rect.text{fill:none;stroke-width:0;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .icon-shape,#mermaid-svg-4R4g7ik1ayQ7B0Dt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .icon-shape p,#mermaid-svg-4R4g7ik1ayQ7B0Dt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .icon-shape .label rect,#mermaid-svg-4R4g7ik1ayQ7B0Dt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4R4g7ik1ayQ7B0Dt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4R4g7ik1ayQ7B0Dt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4R4g7ik1ayQ7B0Dt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} indexless: 索引为空
radosgw-admin bucket rm --purge-objects
rgw_remove_bucket (rgw_bucket.cc:549)
list_op.list_objects() 读索引
objs = 空
循环体不执行 → 对象 0 删除
delete_bucket() 删元数据+空索引
命令 exit 0, 数据对象全部成为孤儿
3. 两个更危险的附带事实
- 连
--purge-objects都不需要 :因为索引为空,rgw_bucket.cc:584的!objs.empty() && !delete_children → -ENOTEMPTY保护永远不会触发,所以裸bucket rm也会直接删掉 bucket 元数据。 - 用
bucket unindex转成 indexless 的情况更糟 :switch_bucket_to_indexless(rgw_admin.cc:1509)会生成新的 bucket_id ,旧索引对象(里面可能还躺着所有对象名)被明确告知"不会删除、需手动清理"(rgw_admin.cc:1532-1533)。新 bucket 实例指向的新索引是空的,bucket rm同样一无所获。
4. 数据怎么才能真正回收
-
从S3角度删除:
使用indexless的桶一般是对象数目量级很大的业务场景,这种业务一般会自己维护对象列表,可以从S3角度直接删除对象列表。
-
或者直接在 pool 层按 bucket_id 前缀手动清:
bash
rados -p <data-pool> ls | grep <bucket_id> | xargs -I{} rados -p <data-pool> rm {}