You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WordPress中update_option并发调用问题:用户邮箱未正确添加至列表

解决方案:WordPress并发场景下的邮箱列表更新问题

问题根源

你的代码存在竞态条件:当两个请求同时触发tw_add_user_to_submitted_today_list时,都会先调用get_option读取到相同的初始邮箱列表,各自添加新邮箱后,后执行的update_option会覆盖先执行的结果,导致其中一个用户的邮箱丢失。

可行解决方案

1. 临时锁机制(基于WordPress缓存)

通过wp_cache_add创建临时锁,确保同一时间只有一个进程能修改tw_settings选项,避免并发冲突:

function tw_add_user_to_submitted_today_list($user_email){
    // 创建锁,有效期5秒(可根据实际调整)
    $lock_key = 'tw_submitted_today_lock';
    if (wp_cache_add($lock_key, true, '', 5)) {
        try {
            $tw_settings = get_option("tw_settings");
            // 确保submitted_today数组存在
            if (!isset($tw_settings['submitted_today'])) {
                $tw_settings['submitted_today'] = [];
            }
            if (!in_array($user_email, $tw_settings['submitted_today'])){
                array_push($tw_settings['submitted_today'], $user_email);
                update_option("tw_settings", $tw_settings);
            }
        } finally {
            // 释放锁
            wp_cache_delete($lock_key);
        }
    } else {
        // 等待100ms后重试(最多重试3次)
        $retry_count = 0;
        while ($retry_count < 3) {
            usleep(100000);
            if (wp_cache_add($lock_key, true, '', 5)) {
                tw_add_user_to_submitted_today_list($user_email);
                break;
            }
            $retry_count++;
        }
    }
}

2. 数据库原子操作(推荐)

绕过get_option的读取,直接通过$wpdb执行数据库层面的原子更新,在SQL里完成邮箱存在性检查和插入,从根源避免竞态:

function tw_add_user_to_submitted_today_list($user_email){
    global $wpdb;
    $option_name = 'tw_settings';
    $option_value = $wpdb->get_var($wpdb->prepare(
        "SELECT option_value FROM $wpdb->options WHERE option_name = %s",
        $option_name
    ));

    if ($option_value) {
        $tw_settings = maybe_unserialize($option_value);
        if (!isset($tw_settings['submitted_today'])) {
            $tw_settings['submitted_today'] = [];
        }
        if (!in_array($user_email, $tw_settings['submitted_today'])) {
            $tw_settings['submitted_today'][] = $user_email;
            // 使用REPLACE实现原子更新
            $wpdb->replace(
                $wpdb->options,
                [
                    'option_name' => $option_name,
                    'option_value' => maybe_serialize($tw_settings),
                    'autoload' => 'yes'
                ],
                ['%s', '%s', '%s']
            );
        }
    } else {
        // 选项不存在时初始化
        $tw_settings = ['submitted_today' => [$user_email]];
        add_option($option_name, $tw_settings);
    }
}

注:REPLACE会先删除旧记录再插入新记录,确保操作原子性;也可以用UPDATE配合option_value旧值作为条件,进一步保证数据一致性。

3. 改用自定义数据库表(适合大规模场景)

如果活动长期运行且用户量较大,将每日提交的邮箱存储到自定义表中会更高效,也天然避免option的并发问题:

// 插件激活时执行该函数创建表
function tw_create_submitted_emails_table() {
    global $wpdb;
    $table_name = $wpdb->prefix . 'tw_submitted_emails';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table_name (
        id mediumint(9) NOT NULL AUTO_INCREMENT,
        user_email varchar(100) NOT NULL,
        submitted_date date NOT NULL,
        PRIMARY KEY  (id),
        UNIQUE KEY email_date (user_email, submitted_date)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
}

// 添加邮箱的函数
function tw_add_user_to_submitted_today_list($user_email){
    global $wpdb;
    $table_name = $wpdb->prefix . 'tw_submitted_emails';
    $today = current_time('Y-m-d');

    // 利用UNIQUE约束,原子插入(已存在则自动忽略)
    $wpdb->insert(
        $table_name,
        [
            'user_email' => $user_email,
            'submitted_date' => $today
        ],
        ['%s', '%s']
    );
}

这种方式通过数据库的UNIQUE约束确保同一用户每日只能提交一次,插入操作是原子性的,完全避免并发问题,同时查询效率也更高。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 11:45:26