Couchbase文档删除 #

一、概述 #

1.1 删除方式 #

Couchbase提供多种文档删除方式:

方式 说明 使用场景
DELETE N1QL删除语句 条件删除、批量删除
remove() SDK删除方法 单个文档删除
软删除 标记删除状态 需要保留历史记录

1.2 删除注意事项 #

text
删除前请注意:
1. 删除操作不可恢复(除非有备份)
2. 删除会释放内存和磁盘空间
3. 删除后索引会自动更新
4. 考虑使用软删除保留历史

二、DELETE语句 #

2.1 基本语法 #

sql
DELETE FROM `keyspace`
WHERE condition;

2.2 删除单个文档 #

sql
DELETE FROM `my-bucket`.`_default`.`_default`
WHERE META().id = 'user::001';

DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'user' AND email = 'test@example.com';

2.3 使用RETURNING #

sql
DELETE FROM `my-bucket`.`_default`.`_default`
WHERE META().id = 'user::001'
RETURNING META().id AS deleted_id, *;

2.4 条件删除 #

sql
DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'user' AND status = 'inactive';

DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'session' 
  AND created_at < DATE_ADD_STR(NOW_STR(), -7, 'day');

DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'log' AND level = 'debug';

2.5 使用USE KEYS #

sql
DELETE FROM `my-bucket`.`_default`.`_default`
USE KEYS 'user::001';

DELETE FROM `my-bucket`.`_default`.`_default`
USE KEYS ['user::001', 'user::002', 'user::003'];

三、批量删除 #

3.1 删除所有匹配文档 #

sql
DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'temp_data';

DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'cache' AND expires_at < NOW_STR();

3.2 限制删除数量 #

sql
DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'log'
LIMIT 1000;

3.3 使用子查询删除 #

sql
DELETE FROM `my-bucket`.`_default`.`_default` AS o
WHERE o.type = 'order'
  AND o.user_id IN (
    SELECT META().id 
    FROM `my-bucket`.`_default`.`_default`
    WHERE type = 'user' AND status = 'deleted'
  );

3.4 分批删除 #

python
import time

def batch_delete(collection, batch_size=1000):
    while True:
        result = cluster.query('''
            DELETE FROM `my-bucket`.`_default`.`_default`
            WHERE type = 'temp_data'
            LIMIT $batch_size
            RETURNING META().id
        ''', QueryOptions(named_parameters={'batch_size': batch_size}))
        
        deleted_count = len(list(result.rows()))
        if deleted_count == 0:
            break
        
        print(f'已删除 {deleted_count} 条记录')
        time.sleep(0.1)

四、SDK删除操作 #

4.1 Python SDK #

python
from couchbase.cluster import Cluster, ClusterOptions
from couchbase.auth import PasswordAuthenticator
from couchbase.options import RemoveOptions
from couchbase.exceptions import DocumentNotFoundException, CasMismatchException

cluster = Cluster(
    'couchbase://localhost',
    ClusterOptions(PasswordAuthenticator('Administrator', 'password'))
)

bucket = cluster.bucket('my-bucket')
collection = bucket.default_collection()

collection.remove('user::001')

try:
    result = collection.get('user::001')
    collection.remove('user::001', RemoveOptions(cas=result.cas))
except DocumentNotFoundException:
    print('文档不存在')
except CasMismatchException:
    print('文档已被修改')

try:
    collection.remove('user::999')
except DocumentNotFoundException:
    print('文档不存在')

4.2 Node.js SDK #

javascript
const couchbase = require('couchbase');

const cluster = new couchbase.Cluster('couchbase://localhost', {
    username: 'Administrator',
    password: 'password'
});

const bucket = cluster.bucket('my-bucket');
const collection = bucket.defaultCollection();

await collection.remove('user::001');

try {
    const result = await collection.get('user::001');
    await collection.remove('user::001', { cas: result.cas });
} catch (error) {
    if (error instanceof couchbase.DocumentNotFoundError) {
        console.log('文档不存在');
    } else if (error instanceof couchbase.CasMismatchError) {
        console.log('文档已被修改');
    }
}

try {
    await collection.remove('user::999');
} catch (error) {
    if (error instanceof couchbase.DocumentNotFoundError) {
        console.log('文档不存在');
    }
}

4.3 Java SDK #

java
import com.couchbase.client.java.*;
import com.couchbase.client.java.kv.*;
import com.couchbase.client.java.exceptions.*;

Cluster cluster = Cluster.connect(
    "localhost",
    ClusterOptions.clusterOptions("Administrator", "password")
);

Bucket bucket = cluster.bucket("my-bucket");
Collection collection = bucket.defaultCollection();

collection.remove("user::001");

try {
    GetResult result = collection.get("user::001");
    collection.remove(
        "user::001", 
        RemoveOptions.removeOptions().cas(result.cas())
    );
} catch (DocumentNotFoundException e) {
    System.out.println("文档不存在");
} catch (CasMismatchException e) {
    System.out.println("文档已被修改");
}

try {
    collection.remove("user::999");
} catch (DocumentNotFoundException e) {
    System.out.println("文档不存在");
}

五、软删除 #

5.1 软删除概念 #

软删除不是真正删除文档,而是标记删除状态:

json
{
    "type": "user",
    "name": "张三",
    "email": "zhangsan@example.com",
    "deleted": true,
    "deleted_at": "2024-01-20T15:00:00Z"
}

5.2 实现软删除 #

sql
UPDATE `my-bucket`.`_default`.`_default`
SET 
    deleted = true,
    deleted_at = NOW_STR(),
    status = 'deleted'
WHERE type = 'user' AND META().id = 'user::001';

5.3 查询时排除已删除 #

sql
SELECT * FROM `my-bucket`.`_default`.`_default`
WHERE type = 'user' AND (deleted = false OR deleted IS MISSING);

CREATE INDEX idx_users_active 
ON `my-bucket`.`_default`.`_default`(name)
WHERE type = 'user' AND (deleted = false OR deleted IS MISSING);

5.4 恢复软删除 #

sql
UPDATE `my-bucket`.`_default`.`_default`
SET 
    deleted = false,
    deleted_at = null,
    status = 'active',
    restored_at = NOW_STR()
WHERE type = 'user' AND META().id = 'user::001';

5.5 永久删除软删除数据 #

sql
DELETE FROM `my-bucket`.`_default`.`_default`
WHERE deleted = true 
  AND deleted_at < DATE_ADD_STR(NOW_STR(), -30, 'day');

六、级联删除 #

6.1 删除关联文档 #

sql
DELETE FROM `my-bucket`.`_default`.`_default` AS d
WHERE d.type = 'order_item' 
  AND d.order_id = 'order::001';

DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'order' AND META().id = 'order::001';

6.2 使用事务级联删除 #

python
from couchbase.transactions import Transactions
from couchbase.transactions.config import TransactionsConfig

transactions = Transactions(cluster, TransactionsConfig())

def delete_order_with_items(order_id):
    def txn_logic(ctx):
        ctx.remove(ctx.get(collection, order_id))
        
        items = cluster.query('''
            SELECT META().id 
            FROM `my-bucket`.`_default`.`_default`
            WHERE type = 'order_item' AND order_id = $order_id
        ''', QueryOptions(named_parameters={'order_id': order_id}))
        
        for item in items.rows():
            ctx.remove(ctx.get(collection, item['id']))
    
    transactions.run(txn_logic)

七、过期自动删除 #

7.1 设置文档过期 #

sql
INSERT INTO `my-bucket`.`_default`.`_default` (KEY, VALUE, OPTIONS)
VALUES (
    'session::abc123',
    {'user_id': 'user::001', 'token': 'abc123'},
    {'expiration': 3600}
);

7.2 SDK设置过期 #

python
from datetime import timedelta
from couchbase.options import UpsertOptions

collection.upsert(
    'session::abc123',
    {'user_id': 'user::001', 'token': 'abc123'},
    UpsertOptions(expiry=timedelta(hours=1))
)

collection.get_and_touch('session::abc123', timedelta(hours=2))

7.3 更新过期时间 #

python
collection.touch('session::abc123', timedelta(hours=1))

八、删除性能优化 #

8.1 使用索引加速删除 #

sql
CREATE INDEX idx_users_status 
ON `my-bucket`.`_default`.`_default`(status)
WHERE type = 'user';

DELETE FROM `my-bucket`.`_default`.`_default`
WHERE type = 'user' AND status = 'inactive';

8.2 分批删除大量数据 #

python
def delete_in_batches(query, batch_size=1000):
    total_deleted = 0
    while True:
        result = cluster.query(f'''
            DELETE FROM `my-bucket`.`_default`.`_default`
            WHERE {query}
            LIMIT {batch_size}
            RETURNING META().id
        ''')
        
        deleted = list(result.rows())
        if not deleted:
            break
        
        total_deleted += len(deleted)
        print(f'已删除 {len(deleted)} 条,总计 {total_deleted} 条')
        
        time.sleep(0.1)
    
    return total_deleted

delete_in_batches("type = 'log' AND created_at < '2024-01-01'")

8.3 异步删除 #

python
import asyncio
from acouchbase.cluster import Cluster

async def async_delete(keys):
    cluster = await Cluster.connect(
        'couchbase://localhost',
        ClusterOptions(PasswordAuthenticator('Administrator', 'password'))
    )
    
    bucket = cluster.bucket('my-bucket')
    collection = bucket.default_collection()
    
    tasks = [collection.remove(key) for key in keys]
    await asyncio.gather(*tasks, return_exceptions=True)

asyncio.run(async_delete(['user::001', 'user::002', 'user::003']))

九、删除与索引 #

9.1 删除后索引更新 #

text
删除文档时:
1. 文档从Bucket中删除
2. 相关索引自动更新
3. 索引项被移除

注意:大量删除可能导致索引碎片化

9.2 索引维护 #

sql
BUILD INDEX ON `my-bucket`.`_default`.`_default`(idx_users_status);

ALTER INDEX idx_users_status 
ON `my-bucket`.`_default`.`_default`
WITH {"action": "cleanup"};

十、常见错误处理 #

10.1 文档不存在 #

python
from couchbase.exceptions import DocumentNotFoundException

try:
    collection.remove('user::999')
except DocumentNotFoundException:
    print('文档不存在,无需删除')

10.2 CAS不匹配 #

python
from couchbase.exceptions import CasMismatchException

def remove_with_retry(key, max_retries=3):
    for _ in range(max_retries):
        try:
            result = collection.get(key)
            collection.remove(key, RemoveOptions(cas=result.cas))
            return True
        except CasMismatchException:
            continue
        except DocumentNotFoundException:
            return True
    return False

10.3 删除权限不足 #

python
from couchbase.exceptions import PermissionDeniedException

try:
    collection.remove('admin::config')
except PermissionDeniedException:
    print('没有删除权限')

十一、删除最佳实践 #

11.1 删除前备份 #

sql
INSERT INTO `my-bucket`.`_default`.`backup` (KEY, VALUE)
SELECT 'backup::' || META().id, _default
FROM `my-bucket`.`_default`.`_default`
WHERE type = 'user' AND status = 'inactive';

11.2 使用事务保证一致性 #

python
from couchbase.transactions import Transactions

def safe_delete_with_related(main_id):
    def txn_logic(ctx):
        ctx.remove(ctx.get(collection, main_id))
        
        related = cluster.query('''
            SELECT META().id 
            FROM `my-bucket`.`_default`.`_default`
            WHERE related_id = $id
        ''', QueryOptions(named_parameters={'id': main_id}))
        
        for item in related.rows():
            ctx.remove(ctx.get(collection, item['id']))
    
    transactions.run(txn_logic)

11.3 审计日志 #

python
def delete_with_audit(key, operator):
    result = collection.get(key)
    doc = result.content
    
    audit_doc = {
        'type': 'delete_audit',
        'document_id': key,
        'document_content': doc,
        'operator': operator,
        'deleted_at': NOW_STR()
    }
    
    collection.upsert(f'audit::delete::{key}::{NOW_STR()}', audit_doc)
    collection.remove(key)

十二、总结 #

删除操作要点:

操作 说明 使用场景
DELETE N1QL删除 批量删除、条件删除
remove() SDK删除 单个文档删除
软删除 标记删除 需要保留历史
过期删除 自动清理 临时数据

最佳实践:

  1. 重要数据使用软删除
  2. 大量删除分批进行
  3. 删除前考虑备份
  4. 使用事务保证一致性
  5. 添加审计日志

下一步,让我们学习基础查询操作!

最后更新:2026-03-27