读写延迟”和“并发”问题通常指的是在数据库操作中特别是在主从复制架构或高并发场景下出现的经典问题。下面将详细解析这些问题的表现、原因和解决方案 问题表现读写延迟主从复制延迟-- 场景刚写入的数据立即查询却查不到 -- 1. 写入主库 INSERT INTO users (name, email) VALUES (张三, zhangsanexample.com); -- 2. 立即从从库查询可能查询失败 SELECT * FROM users WHERE email zhangsanexample.com; -- 查不到并发问题// 场景库存超卖 // 用户A和用户B同时购买最后一件商品 $stock DB::table(products)-where(id, 1)-value(stock); // 同时读到 stock1 // 用户A if ($stock 0) { DB::table(products)-where(id, 1)-decrement(stock); // 处理订单... } // 用户B同时执行 if ($stock 0) { DB::table(products)-where(id, 1)-decrement(stock); // 库存变成 -1 } 问题诊断流程flowchart TD A[用户遇到数据不一致] -- B{检查问题类型} B --|立即写入后查不到| C[读写延迟br主从同步问题] B --|数据计数错误/超卖| D[并发竞争问题] B --|随机性失败| E[网络/连接池问题] C -- F[解决方案: 强制主库读取] D -- G[解决方案: 事务锁/队列] E -- H[解决方案: 连接优化/重试]下面详细说明各类问题。1. 读写延迟主从复制延迟原因分析主从同步时间差数据写入主库后需要时间同步到从库网络延迟主从服务器之间的网络延迟从库负载过高从库处理查询请求过多同步变慢大事务延迟大量数据写入导致同步缓慢解决方案方案1强制从主库读取// Laravel 中强制使用主库 // 方法1使用 writeConnection $user DB::connection(mysql::write)-table(users)-find(1); // 方法2使用 onWriteConnection $user User::onWriteConnection()-find(1); // 方法3设置整个查询使用主库 DB::connection(mysql)-select(SET SESSION TRANSACTION READ WRITE); $users DB::table(users)-get();方案2使用 sticky 连接Laravel 8.25// config/database.php mysql [ sticky true, // 启用粘性连接 read [ [host read1.example.com], [host read2.example.com] ], write [ host write.example.com ], ], // 写入后的短时间内同一请求的读操作会使用主库方案3延迟重试策略function getWithRetry($id, $maxRetries 3) { for ($i 0; $i $maxRetries; $i) { $data User::find($id); if ($data) { return $data; } // 延迟后重试 if ($i $maxRetries - 1) { usleep(100000 * pow(2, $i)); // 指数退避 } } // 最后一次尝试从主库读取 return User::onWriteConnection()-find($id); }2. 并发问题原因分析竞态条件多个进程同时读取和修改同一数据无锁更新UPDATE 操作没有适当的锁机制非原子操作先读后写的非原子操作解决方案方案1使用数据库事务 行锁// 悲观锁SELECT ... FOR UPDATE DB::transaction(function () { // 锁定行 $product DB::table(products) -where(id, 1) -lockForUpdate() // 行级锁 -first(); if ($product-stock 0) { DB::table(products) -where(id, 1) -decrement(stock); // 创建订单... } }); // 或者使用 sharedLock共享锁 $product DB::table(products) -where(id, 1) -sharedLock() -first();方案2使用原子操作// 使用原子更新避免先读后写 $affected DB::table(products) -where(id, 1) -where(stock, , 0) -decrement(stock); if ($affected 0) { // 库存减少成功处理订单 } else { // 库存不足 } // 使用 CASE 语句 DB::table(products) -where(id, 1) -update([ stock DB::raw(CASE WHEN stock 0 THEN stock - 1 ELSE stock END) ]);方案3使用 Redis 分布式锁use Illuminate\Support\Facades\Redis; function purchaseWithLock($productId, $userId) { $lockKey purchase:lock:{$productId}; $lock Redis::set($lockKey, $userId, NX, EX, 10); // 10秒超时 if (!$lock) { throw new Exception(系统繁忙请稍后重试); } try { DB::transaction(function () use ($productId) { $product Product::where(id, $productId) -where(stock, , 0) -first(); if ($product) { $product-decrement(stock); // 创建订单... } }); } finally { Redis::del($lockKey); } }方案4使用队列串行处理// 将并发请求转为串行处理 class ProcessPurchase implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public function handle() { DB::transaction(function () { $product Product::where(id, $this-productId) -where(stock, , 0) -first(); if ($product) { $product-decrement(stock); // 创建订单... } }); } } // 触发购买 ProcessPurchase::dispatch($productId, $userId);3. 混合问题解决方案完整的最佳实践class PurchaseService { public function purchase($productId, $userId, $quantity 1) { // 1. 快速失败检查不涉及数据库 if ($quantity 0) { throw new InvalidArgumentException(数量必须大于0); } // 2. 使用数据库事务保证原子性 return DB::transaction(function () use ($productId, $userId, $quantity) { // 3. 使用行级锁防止并发 $product Product::where(id, $productId) -lockForUpdate() // 悲观锁 -first(); if (!$product) { throw new ModelNotFoundException(商品不存在); } if ($product-stock $quantity) { throw new Exception(库存不足); } // 4. 原子更新 $product-decrement(stock, $quantity); // 5. 创建订单 $order Order::create([ user_id $userId, product_id $productId, quantity $quantity, status paid ]); // 6. 清理缓存 Cache::forget(product:{$productId}); return $order; }, 3); // 重试3次 } } 监控与诊断监控指标// 监控数据库延迟 class DatabaseMonitor { public static function checkReplicationDelay() { // 检查主从延迟 $delay DB::select(SHOW SLAVE STATUS); if ($delay[Seconds_Behind_Master] 5) { Log::warning(数据库主从延迟过高, [ delay $delay[Seconds_Behind_Master], master config(database.connections.mysql.write.host), slave config(database.connections.mysql.read.host) ]); } } public static function logSlowQueries() { // 启用慢查询日志 DB::enableQueryLog(); // 执行查询后 $queries DB::getQueryLog(); foreach ($queries as $query) { if ($query[time] 1000) { // 超过1秒 Log::warning(慢查询, $query); } } } }调试技巧// 在代码中添加调试信息 DB::listen(function ($query) { Log::info(SQL Query, [ sql $query-sql, bindings $query-bindings, time $query-time, connection $query-connectionName ]); }); // 检查当前使用的连接 $connection DB::getDefaultConnection(); $isWriting DB::connection()-getPdo()-inTransaction(); 总结与建议问题快速定位问题现象可能原因快速验证刚写入的数据查不到读写分离延迟直接从主库查询是否能查到数据计数错误/超卖并发竞争添加行锁后问题是否消失随机性失败连接池/网络检查连接数和超时设置预防措施设计阶段重要业务操作使用事务避免在事务中进行远程调用合理设计数据库索引开发阶段读写分离场景下对一致性要求高的读操作强制走主库更新操作使用原子操作高并发场景使用队列或锁运维阶段监控主从延迟设置合理的超时时间定期优化数据库紧急处理// 临时解决方案全局强制走主库 // 在 AppServiceProvider 中 public function boot() { if (app()-environment(production) request()-has(debug_read)) { DB::connection(mysql)-setReadConnection(write); } }如果您的具体问题是“写入后立即查询返回空”那几乎可以确定是读写分离延迟问题解决方案是让这个查询强制走主库连接。