You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于Redisson RMap Write Through缓存写入顺序及异常处理的技术咨询

How to Fix Redisson Write Through's Cache-First Behavior (Ensure DB Success Before Cache Update)

First off, let's clarify what's happening here: Redisson's default WRITE_THROUGH mode is designed to update the Redis cache first, then trigger your MapWriter to sync the change to the database. This is intentional per the Write Through pattern definition, but as you've noticed, if the database operation fails, the cache already holds the new value—creating an inconsistency between cache and DB.

To achieve your desired flow (update DB first, then cache only if DB succeeds), here are two reliable approaches:

Approach 1: Manual Control (Simplest & Most Reliable)

The cleanest solution is to bypass Redisson's built-in Write Through entirely and handle the order yourself. This gives you full control over the transaction flow and avoids any unexpected behavior from Redisson's internal logic.

Here's a quick code example:

// Inject your RMap, database service, and any necessary transaction managers
@Autowired
private RMap<String, YourEntity> yourRMap;

@Autowired
private YourDbService dbService;

public void updateData(String key, YourEntity newValue) {
    // Wrap in a database transaction if needed (e.g., Spring @Transactional)
    try {
        // Step 1: Update the database first
        dbService.updateEntity(key, newValue);
        
        // Step 2: Only update the cache if the DB operation succeeded
        yourRMap.put(key, newValue);
    } catch (DbException e) {
        // Log the failure and propagate the error—cache remains untouched
        log.error("Failed to update database for key: {}", key, e);
        throw new ServiceException("Data update failed", e);
    }
}

If you're using Spring, adding the @Transactional annotation to this method will ensure the DB rolls back if any exception occurs, and the cache update is skipped entirely.

Approach 2: Custom MapWriter with Cache Rollback

If you want to keep using Redisson's Write Through infrastructure, you can create a custom MapWriter that rolls back the cache change if the DB operation fails. Note: This approach requires handling concurrency carefully to avoid race conditions.

Here's how you could implement it:

public class RollbackableMapWriter implements MapWriter<String, YourEntity> {

    private final RMap<String, YourEntity> rMap;
    private final YourDbService dbService;

    public RollbackableMapWriter(RMap<String, YourEntity> rMap, YourDbService dbService) {
        this.rMap = rMap;
        this.dbService = dbService;
    }

    @Override
    public void write(Map<String, YourEntity> entries) {
        // Capture the old cache values before proceeding
        Map<String, YourEntity> oldValues = rMap.getAll(entries.keySet());
        
        try {
            // Execute the database update
            dbService.batchUpdate(entries);
        } catch (DbException e) {
            // Rollback the cache to its previous state
            restoreCache(oldValues, entries.keySet());
            throw new RuntimeException("Database write failed; cache rolled back", e);
        }
    }

    @Override
    public void delete(Collection<String> keys) {
        Map<String, YourEntity> oldValues = rMap.getAll(keys);
        
        try {
            dbService.batchDelete(keys);
        } catch (DbException e) {
            restoreCache(oldValues, keys);
            throw new RuntimeException("Database delete failed; cache rolled back", e);
        }
    }

    private void restoreCache(Map<String, YourEntity> oldValues, Collection<String> keys) {
        if (!oldValues.isEmpty()) {
            // Restore existing entries to their previous values
            rMap.putAll(oldValues);
        } else {
            // Remove any newly added keys that didn't exist before
            rMap.fastRemove(keys.toArray());
        }
    }
}

Important Notes for Approach 2:

  • Concurrency: To prevent race conditions (e.g., another thread modifying the same key while you're rolling back), wrap the entire write/rollback logic in a Redisson distributed lock for each key.
  • Performance: Capturing old values and rolling back adds overhead compared to Approach 1, so only use this if you need to retain Redisson's Write Through integration for other reasons.

内容的提问来源于stack exchange,提问作者Megatron

火山引擎 最新活动