首页
4K壁纸
直播
统计分析
友情链接
搜索
1
#1031 – TABLE STORAGE ENGINE FOR ” DOESN’T HAVE THIS OPTION解决方法
1,224 阅读
2
让浏览器不显示 https 页面中 http 请求警报 http-equiv=”Content-Security-Policy” content=”upgrade-insecure-requests”
941 阅读
3
报错代码:ERROR 1227 (42000)-解决办法
730 阅读
4
微信个人商户号养号建议
580 阅读
5
解决移动端position:fixed随软键盘移动的问题
550 阅读
Php
Mysql
Linux
Reids
Java
Python
常用笔记
学习
乱七八糟
Search
标签搜索
php
千卡云支付
Mysql
Linux
redis
千卡云
千卡易支付
function
Nginx
shell
JS
JSON
跨域
支付宝
CentOS
Apache
支付
composer
Array
database
蓝科迪梦
累计撰写
98
篇文章
累计收到
0
条评论
首页
栏目
Php
Mysql
Linux
Reids
Java
Python
常用笔记
学习
乱七八糟
页面
4K壁纸
直播
统计分析
友情链接
搜索到
2
篇与
的结果
2025-10-08
PHP开发中数据库事务死锁与并发控制
PHP开发中的复杂问题及解决方案:数据库事务死锁与并发控制在复杂的Web应用中,数据库事务死锁是一个常见且难以调试的问题。当多个事务同时竞争相同的资源时,可能会导致系统性能下降甚至完全阻塞。死锁产生的原因1. 交叉锁定资源// 事务A: 先更新用户表,再更新订单表 // 事务B: 先更新订单表,再更新用户表 // 这种情况下容易产生死锁2. 不一致的访问顺序class OrderService { public function updateOrderAndUser($orderId, $userId) { // 不同的操作顺序可能导致死锁 $this->updateOrder($orderId); $this->updateUser($userId); } }解决方案方案一:统一资源访问顺序/** * 死锁预防 - 统一资源访问顺序 */ class DeadlockPreventionService { private PDO $db; public function __construct(PDO $db) { $this->db = $db; } /** * 按照固定顺序访问资源 */ public function updateUserAndOrder($userId, $orderId, $userData, $orderData) { // 始终按照ID大小顺序访问资源 $resources = [ ['type' => 'user', 'id' => $userId], ['type' => 'order', 'id' => $orderId] ]; // 按ID排序确保访问顺序一致 usort($resources, function($a, $b) { return $a['id'] <=> $b['id']; }); try { $this->db->beginTransaction(); foreach ($resources as $resource) { if ($resource['type'] === 'user') { $this->updateUserRecord($resource['id'], $userData); } elseif ($resource['type'] === 'order') { $this->updateOrderRecord($resource['id'], $orderData); } } $this->db->commit(); return true; } catch (Exception $e) { $this->db->rollback(); throw $e; } } private function updateUserRecord($userId, $userData) { $stmt = $this->db->prepare("UPDATE users SET name = ?, email = ? WHERE id = ?"); $stmt->execute([$userData['name'], $userData['email'], $userId]); } private function updateOrderRecord($orderId, $orderData) { $stmt = $this->db->prepare("UPDATE orders SET status = ?, amount = ? WHERE id = ?"); $stmt->execute([$orderData['status'], $orderData['amount'], $orderId]); } }方案二:重试机制处理死锁/** * 死锁自动重试机制 */ class DeadlockRetryHandler { private PDO $db; private int $maxRetries; private int $baseDelayMs; public function __construct(PDO $db, int $maxRetries = 3, int $baseDelayMs = 100) { $this->db = $db; $this->maxRetries = $maxRetries; $this->baseDelayMs = $baseDelayMs; } /** * 执行带死锁重试的数据库操作 */ public function executeWithRetry(callable $operation) { $attempt = 0; while ($attempt < $this->maxRetries) { try { return $operation(); } catch (PDOException $e) { // 检查是否为死锁错误 if ($this->isDeadlockError($e) && $attempt < $this->maxRetries - 1) { $delay = $this->calculateExponentialBackoff($attempt); usleep($delay * 1000); // 转换为微秒 $attempt++; continue; } throw $e; } } } /** * 判断是否为死锁错误 */ private function isDeadlockError(PDOException $e): bool { $errorCode = $e->getCode(); // MySQL死锁错误码: 1213 // PostgreSQL死锁错误码: 40P01 // SQL Server死锁错误码: 1205 return in_array($errorCode, [1213, '40P01', 1205]); } /** * 计算指数退避延迟 */ private function calculateExponentialBackoff(int $attempt): int { $delay = $this->baseDelayMs * pow(2, $attempt); $jitter = rand(0, $this->baseDelayMs); return $delay + $jitter; } } // 使用示例 class OrderProcessingService { private DeadlockRetryHandler $retryHandler; public function __construct(PDO $db) { $this->retryHandler = new DeadlockRetryHandler($db); } public function processComplexOrder($orderId) { return $this->retryHandler->executeWithRetry(function() use ($orderId) { // 复杂的订单处理逻辑,可能涉及多个表的更新 $this->performComplexOrderOperations($orderId); }); } }方案三:乐观锁实现/** * 乐观锁实现 - 版本号机制 */ class OptimisticLockingService { private PDO $db; public function __construct(PDO $db) { $this->db = $db; } /** * 使用乐观锁更新用户信息 */ public function updateUserWithOptimisticLock($userId, $newData, $expectedVersion) { $stmt = $this->db->prepare(" UPDATE users SET name = ?, email = ?, version = version + 1 WHERE id = ? AND version = ? "); $stmt->execute([ $newData['name'], $newData['email'], $userId, $expectedVersion ]); $affectedRows = $stmt->rowCount(); if ($affectedRows === 0) { // 版本号不匹配,说明数据已被其他事务修改 throw new ConcurrentModificationException( "User data was modified by another transaction" ); } return true; } /** * 获取用户数据及版本号 */ public function getUserWithVersion($userId) { $stmt = $this->db->prepare("SELECT *, version FROM users WHERE id = ?"); $stmt->execute([$userId]); return $stmt->fetch(PDO::FETCH_ASSOC); } } class ConcurrentModificationException extends Exception {} // 使用乐观锁的服务 class UserService { private OptimisticLockingService $lockingService; public function __construct(OptimisticLockingService $lockingService) { $this->lockingService = $lockingService; } public function updateUserSafely($userId, $userData) { $maxAttempts = 3; $attempts = 0; while ($attempts < $maxAttempts) { try { $user = $this->lockingService->getUserWithVersion($userId); $this->lockingService->updateUserWithOptimisticLock( $userId, $userData, $user['version'] ); return true; } catch (ConcurrentModificationException $e) { $attempts++; if ($attempts >= $maxAttempts) { throw new Exception("Failed to update user after {$maxAttempts} attempts"); } // 短暂等待后重试 usleep(rand(10000, 50000)); // 10-50ms } } } }方案四:读写分离与连接池管理/** * 数据库连接池管理器 */ class DatabaseConnectionPool { private array $writeConnections = []; private array $readConnections = []; private array $config; public function __construct(array $config) { $this->config = $config; $this->initializeConnections(); } private function initializeConnections() { // 初始化写连接(主库) for ($i = 0; $i < $this->config['write_pool_size']; $i++) { $this->writeConnections[] = $this->createWriteConnection(); } // 初始化读连接(从库) for ($i = 0; $i < $this->config['read_pool_size']; $i++) { $this->readConnections[] = $this->createReadConnection(); } } private function createWriteConnection(): PDO { $dsn = $this->config['master_dsn']; return new PDO($dsn, $this->config['username'], $this->config['password'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5 ]); } private function createReadConnection(): PDO { // 轮询选择从库 $slaveDsn = $this->config['slave_dsns'][array_rand($this->config['slave_dsns'])]; return new PDO($slaveDsn, $this->config['username'], $this->config['password'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 5 ]); } /** * 获取写连接 */ public function getWriteConnection(): PDO { // 简单的轮询算法 $connection = array_shift($this->writeConnections); $this->writeConnections[] = $connection; return $connection; } /** * 获取读连接 */ public function getReadConnection(): PDO { $connection = array_shift($this->readConnections); $this->readConnections[] = $connection; return $connection; } } /** * 事务管理器 */ class TransactionManager { private DatabaseConnectionPool $connectionPool; private array $activeTransactions = []; public function __construct(DatabaseConnectionPool $connectionPool) { $this->connectionPool = $connectionPool; } /** * 开始事务 */ public function beginTransaction(): TransactionContext { $connection = $this->connectionPool->getWriteConnection(); $connection->beginTransaction(); $transactionId = uniqid(); $context = new TransactionContext($transactionId, $connection); $this->activeTransactions[$transactionId] = $context; return $context; } /** * 提交事务 */ public function commit(TransactionContext $context) { try { $context->getConnection()->commit(); } finally { unset($this->activeTransactions[$context->getId()]); } } /** * 回滚事务 */ public function rollback(TransactionContext $context) { try { $context->getConnection()->rollback(); } finally { unset($this->activeTransactions[$context->getId()]); } } } class TransactionContext { private string $id; private PDO $connection; public function __construct(string $id, PDO $connection) { $this->id = $id; $this->connection = $connection; } public function getId(): string { return $this->id; } public function getConnection(): PDO { return $this->connection; } }最佳实践建议1. 事务设计原则保持事务短小:减少事务持有锁的时间统一访问顺序:避免交叉锁定合理设置超时:防止长时间阻塞2. 监控和诊断class DeadlockMonitor { public static function logDeadlockInfo($connection) { // MySQL查看死锁信息 $stmt = $connection->query("SHOW ENGINE INNODB STATUS"); $status = $stmt->fetch(); error_log("Deadlock detected: " . $status['Status']); } }3. 配置优化// 数据库配置优化 $databaseConfig = [ 'innodb_lock_wait_timeout' => 50, // 锁等待超时时间 'innodb_deadlock_detect' => 'ON', // 启用死锁检测 'innodb_rollback_on_timeout' => 'ON', // 超时时回滚 ];总结解决数据库死锁问题的核心策略:预防为主:通过统一资源访问顺序避免死锁产生优雅处理:实现重试机制自动恢复替代方案:使用乐观锁减少锁竞争架构优化:读写分离减轻数据库压力监控告警:及时发现和诊断死锁问题通过这些综合措施,可以显著降低数据库死锁的发生概率,提高系统的并发处理能力和稳定性。
2025年10月08日
0 阅读
0 评论
0 点赞
2025-10-08
PHP开发中API接口限流与并发控制
PHP开发中的复杂问题及解决方案在高并发的Web应用中,API接口的限流和并发控制是保证系统稳定性的关键问题。当大量请求同时涌入时,如果没有适当的保护机制,很容易导致系统崩溃或响应缓慢。常见的并发问题场景1. 接口被恶意刷取// 用户反馈:某个API接口被频繁调用,导致服务器负载过高 class ApiController { public function getData() { // 复杂的数据处理逻辑 $result = $this->heavyDatabaseQuery(); return json_encode($result); } }2. 秒杀活动中的超卖问题class OrderController { public function createOrder($productId, $quantity) { $product = ProductModel::find($productId); if ($product->stock >= $quantity) { // 可能在高并发下出现超卖 $product->stock -= $quantity; $product->save(); return ['status' => 'success']; } return ['status' => 'failed']; } }解决方案方案一:基于Redis的令牌桶算法/** * 令牌桶限流器 */ class TokenBucketRateLimiter { private Redis $redis; private string $key; private int $capacity; // 桶容量 private int $rate; // 令牌生成速率(每秒) public function __construct(Redis $redis, string $key, int $capacity, int $rate) { $this->redis = $redis; $this->key = $key; $this->capacity = $capacity; $this->rate = $rate; } /** * 尝试获取令牌 */ public function acquire(int $tokens = 1): bool { $now = microtime(true); $key = "rate_limiter:{$this->key}"; // 使用Lua脚本保证原子性 $script = ' local key = KEYS[1] local capacity = tonumber(ARGV[1]) local rate = tonumber(ARGV[2]) local tokens = tonumber(ARGV[3]) local now = tonumber(ARGV[4]) local data = redis.call("HMGET", key, "tokens", "timestamp") local current_tokens = tonumber(data[1]) or capacity local last_timestamp = tonumber(data[2]) or now -- 计算新增的令牌数 local elapsed = now - last_timestamp local new_tokens = math.floor(elapsed * rate) -- 更新令牌数量 current_tokens = math.min(capacity, current_tokens + new_tokens) if current_tokens >= tokens then current_tokens = current_tokens - tokens redis.call("HMSET", key, "tokens", current_tokens, "timestamp", now) redis.call("EXPIRE", key, 86400) -- 24小时过期 return 1 else redis.call("HMSET", key, "tokens", current_tokens, "timestamp", now) redis.call("EXPIRE", key, 86400) return 0 end '; return (bool) $this->redis->eval($script, [$key, $this->capacity, $this->rate, $tokens, $now], 1); } } // 使用示例 class RateLimitedApiController { private TokenBucketRateLimiter $limiter; public function __construct() { $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $this->limiter = new TokenBucketRateLimiter($redis, 'api:get_data', 100, 10); // 100容量,每秒10个令牌 } public function getData() { // 限流检查 if (!$this->limiter->acquire()) { http_response_code(429); return json_encode(['error' => 'Too Many Requests']); } // 实际业务逻辑 $result = $this->heavyDatabaseQuery(); return json_encode($result); } }方案二:分布式锁防止超卖/** * 基于Redis的分布式锁 */ class RedisDistributedLock { private Redis $redis; private string $lockKey; private string $lockValue; private int $expireTime; public function __construct(Redis $redis, string $lockKey, int $expireTime = 30) { $this->redis = $redis; $this->lockKey = "lock:{$lockKey}"; $this->lockValue = uniqid(php_uname('n'), true); $this->expireTime = $expireTime; } /** * 获取锁 */ public function acquire(): bool { $script = ' local key = KEYS[1] local value = ARGV[1] local expire = ARGV[2] local result = redis.call("SET", key, value, "NX", "EX", expire) if result then return 1 else return 0 end '; return (bool) $this->redis->eval($script, [$this->lockKey, $this->lockValue, $this->expireTime], 1); } /** * 释放锁 */ public function release(): bool { $script = ' local key = KEYS[1] local value = ARGV[1] local current_value = redis.call("GET", key) if current_value == value then redis.call("DEL", key) return 1 else return 0 end '; return (bool) $this->redis->eval($script, [$this->lockKey, $this->lockValue], 1); } /** * 自动续期(看门狗) */ public function renew(): bool { $script = ' local key = KEYS[1] local value = ARGV[1] local expire = ARGV[2] local current_value = redis.call("GET", key) if current_value == value then redis.call("EXPIRE", key, expire) return 1 else return 0 end '; return (bool) $this->redis->eval($script, [$this->lockKey, $this->lockValue, $this->expireTime], 1); } } // 使用分布式锁的安全下单 class SafeOrderController { private Redis $redis; public function __construct() { $this->redis = new Redis(); $this->redis->connect('127.0.0.1', 6379); } public function createOrder($productId, $quantity) { $lock = new RedisDistributedLock($this->redis, "product_{$productId}", 10); // 尝试获取锁 if (!$lock->acquire()) { return ['status' => 'failed', 'message' => 'System busy, please try again']; } try { $product = ProductModel::find($productId); // 检查库存 if ($product->stock >= $quantity) { // 扣减库存 $product->stock -= $quantity; $product->save(); // 创建订单 $order = new OrderModel(); $order->product_id = $productId; $order->quantity = $quantity; $order->save(); return ['status' => 'success', 'order_id' => $order->id]; } else { return ['status' => 'failed', 'message' => 'Insufficient stock']; } } finally { // 释放锁 $lock->release(); } } }方案三:滑动窗口限流/** * 滑动窗口限流器 */ class SlidingWindowRateLimiter { private Redis $redis; private string $key; private int $limit; private int $windowSize; // 窗口大小(秒) public function __construct(Redis $redis, string $key, int $limit, int $windowSize) { $this->redis = $redis; $this->key = "sliding_window:{$key}"; $this->limit = $limit; $this->windowSize = $windowSize; } /** * 检查是否允许请求 */ public function allowRequest(): bool { $now = time(); $minTime = $now - $this->windowSize; $script = ' local key = KEYS[1] local limit = tonumber(ARGV[1]) local min_time = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) -- 移除过期的记录 redis.call("ZREMRANGEBYSCORE", key, 0, min_time) -- 获取当前窗口内的请求数 local current_count = redis.call("ZCARD", key) if current_count < limit then -- 添加当前请求 redis.call("ZADD", key, now, now) redis.call("EXPIRE", key, ARGV[4]) return 1 else return 0 end '; $expireTime = $this->windowSize + 10; // 稍微延长过期时间 return (bool) $this->redis->eval( $script, [$this->key, $this->limit, $minTime, $now, $expireTime], 1 ); } /** * 获取当前窗口内的请求数 */ public function getCurrentCount(): int { $now = time(); $minTime = $now - $this->windowSize; $this->redis->zRemRangeByScore($this->key, 0, $minTime); return $this->redis->zCard($this->key); } } // 应用滑动窗口限流 class SlidingWindowApiController { private SlidingWindowRateLimiter $limiter; public function __construct() { $redis = new Redis(); $redis->connect('127.0.0.1', 6379); // 每分钟最多100次请求 $this->limiter = new SlidingWindowRateLimiter($redis, 'api:endpoint', 100, 60); } public function handleRequest() { if (!$this->limiter->allowRequest()) { http_response_code(429); return json_encode([ 'error' => 'Rate limit exceeded', 'retry_after' => 60, 'current_requests' => $this->limiter->getCurrentCount() ]); } // 处理实际业务逻辑 return $this->processBusinessLogic(); } }最佳实践建议1. 多层次防护策略应用层限流:在业务逻辑层进行初步限制网关层限流:使用Nginx、API Gateway等进行前置限制服务层限流:在具体服务中实施精细化控制2. 监控和告警class RateLimitMonitor { public static function logRateLimitEvent(string $endpoint, string $clientId): void { // 记录限流事件日志 error_log("Rate limit triggered for endpoint: {$endpoint}, client: {$clientId}"); // 发送监控指标 MetricsCollector::increment('rate_limit_triggered', [ 'endpoint' => $endpoint, 'client_id' => $clientId ]); } }3. 配置化管理class RateLimitConfig { private static array $configs = [ 'api:get_data' => ['limit' => 100, 'window' => 60], 'api:create_order' => ['limit' => 10, 'window' => 60], 'default' => ['limit' => 50, 'window' => 60] ]; public static function get(string $endpoint): array { return self::$configs[$endpoint] ?? self::$configs['default']; } }总结API限流和并发控制的关键要点:选择合适的算法:令牌桶适合突发流量,漏桶适合平滑流量,滑动窗口适合精确控制使用分布式存储:Redis等支持原子操作的存储系统确保限流准确性考虑异常处理:在网络分区或系统故障时要有降级策略监控和调优:持续监控限流效果,根据实际使用情况进行参数调整用户体验:合理设置限流阈值,提供友好的错误提示通过这些技术手段,可以有效保护系统免受高并发冲击,确保服务的稳定性和可用性。
2025年10月08日
1 阅读
0 评论
0 点赞