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
相关产品推荐
相关产品推荐

