无尘阁日记

无尘阁日记

多台机器并发修改数据,如何优雅同步?这个缓存方案又快又稳!
2025-02-14

prompt:

你是一个顶级软件工程师,我有这样一个问题,请你帮我设计一个方案。

我有一个接口,他如果通过a,b,c甚至更多机器请求(通过机器码识别),会改变数据库数据,我需要用缓存记录下他们分别已经做了更改这个状态,然后在a请求的时候需要识别到b、c更改了,从而提示需要同步到底稿中,b请求需要识别到a、c更改了从而提示需要同步到底稿中,并且在a同步完要标记他已经同步过,其他不能更改这个待同步状态,其他也一样。请问我描述清楚我的需求了吗?你还有什么不清楚的?

答:

你的需求大致可以归纳如下:

  1. 多个机器(a, b, c, ...)可能会请求一个接口,并更改数据库数据

  2. 需要使用缓存来记录各个机器是否已经更改了数据(即,每台机器的更改状态)。

  3. 当任意机器(例如 a)请求时,需要能够识别其他机器(b, c, ...)已经做了更改,并提示需要同步到底稿。

  4. 当某台机器(如 a)执行了同步操作后,需要标记该机器已经同步,但这个状态不能被其他机器修改。

  5. 其他机器也需要遵循同样的逻辑。

进一步的问题:

  • 数据的唯一性:是针对单个数据条目,还是全局的所有数据?即,a、b、c 修改的是同一条数据,还是可能涉及不同的数据记录?

  • 同步的粒度:同步状态是针对整个数据表,还是某个具体的数据记录?

  • 缓存的持久化策略:是否需要跨进程/服务器共享缓存,或者只是进程内缓存?

  • 缓存的实现方式:是否有特定的缓存方案(如 Redis),还是可以自由选择?

  • 并发处理:如何避免并发问题,比如 a 和 b 在几乎同时检查状态并决定同步?

  • 清理策略:同步状态需要持久存储,还是在某个时间段后自动清除?

你可以再补充这些细节,我可以根据这些情况提供更精准的架构设计。


prompt:

只需要监测a,b,c修改了数据,不需要具体识别什么数据。同步的粒度你也不需要关心。需要跨进程。直接用Yii::$app->cache缓存即可。并发没关系,各自获取同样数据同步各自的即可。会在同步后手动清除。

答:

方案设计

根据你的需求,我们可以使用 Yii::$app->cache 进行缓存,存储每台机器(a, b, c)的修改状态。每次请求时,检查其他机器是否已修改数据,以决定是否提示同步到底稿。

1. 数据存储格式

使用 Yii::$app->cache 以键值对的方式存储修改状态:

[    'machine_changes' => [        'a' => true,  // a 机器修改了数据        'b' => true,  // b 机器修改了数据        'c' => false, // c 机器未修改    ]]
  • 这里 machine_changes 是缓存键,值是一个关联数组,记录每个机器是否修改了数据。

  • 修改数据时,更新该键值对。

  • 当某台机器(如 a)执行同步后,需要清除自身的同步标记。

2. 代码实现

(1) 记录某台机器的修改状态

当 a, b, c 任何一台机器修改数据时,调用以下代码:

function markMachineChanged($machine){    $cacheKey = 'machine_changes';    $changes = Yii::$app->cache->get($cacheKey) ?? [];    // 标记当前机器已修改    $changes[$machine] = true;    // 存入缓存,设置一个适当的过期时间(可根据需求调整)    Yii::$app->cache->set($cacheKey, $changes, 3600);}

调用示例:

markMachineChanged('a');  // 记录 a 机器修改了数据markMachineChanged('b');  // 记录 b 机器修改了数据

(2) 检查其他机器是否已修改

当某台机器请求时,它需要检查其他机器是否有更改,并决定是否提示同步:

function checkForSync($machine){    $cacheKey = 'machine_changes';    $changes = Yii::$app->cache->get($cacheKey) ?? [];    // 其他机器是否有修改    $otherMachinesChanged = array_filter($changes, function ($key) use ($machine) {        return $key !== $machine && $key; // 过滤掉当前机器自身    }, ARRAY_FILTER_USE_KEY);    return !empty($otherMachinesChanged);}

调用示例:

if (checkForSync('a')) {    echo "其他机器修改了数据,需要同步到底稿!";}

(3) 标记某台机器已同步

当某台机器(如 a)同步完成后,它应该标记自己已经同步:

function markMachineSynced($machine){    $cacheKey = 'machine_changes';    $changes = Yii::$app->cache->get($cacheKey) ?? [];    if (isset($changes[$machine])) {        // 标记当前机器已同步(即删除其修改记录)        unset($changes[$machine]);        Yii::$app->cache->set($cacheKey, $changes, 3600);    }}

调用示例:

mark
MachineSynced('a');  // a 机器同步完成后标记

流程总结

  1. 修改数据时:调用 markMachineChanged($machine) 记录当前机器已修改数据。

  2. 请求检查时:调用 checkForSync($machine) 检查其他机器是否已修改数据,并决定是否提示同步到底稿。

  3. 同步完成时:调用 markMachineSynced($machine) 清除当前机器的修改标记。


方案优点

  • 低成本:使用 Yii::$app->cache,避免数据库额外查询,性能高效。

  • 易扩展:支持任意数量的机器(a, b, c, d…)。

  • 简单可靠:无并发问题,每个机器只关心自己的同步状态。

这个方案完全符合你的要求,符合 Yii 框架的缓存机制,可以直接使用。你可以试试看,看看是否符合你的预期?