赣州网站建设培训广东省自然资源厅8号文
2026/4/3 21:51:32 网站建设 项目流程
赣州网站建设培训,广东省自然资源厅8号文,网推平台有哪些,关于征集网站建设素材的通知Redis ThinkPHP 实战学习手册#xff08;含秒杀场景#xff09; 目录基础准备#xff1a;ThinkPHP 集成 RedisRedis 核心数据结构#xff08;ThinkPHP 用法#xff09;秒杀场景核心#xff1a;Redis 原子性与事务ThinkPHP Redis 实战场景#xff08;秒杀 / 缓存 / 限流…Redis ThinkPHP 实战学习手册含秒杀场景目录基础准备ThinkPHP 集成 RedisRedis 核心数据结构ThinkPHP 用法秒杀场景核心Redis 原子性与事务ThinkPHP Redis 实战场景秒杀 / 缓存 / 限流常见问题与面试避坑一、基础准备ThinkPHP 集成 Redis1.1 环境要求ThinkPHP 5.1/6.0推荐 6.0缓存扩展更完善PHP Redis 扩展php_redis.dll需在php.ini中启用Redis 服务本地 / 服务器部署默认端口 63791.2 配置 RedisThinkPHP步骤 1修改配置文件在config/cache.php中配置 Redis 缓存驱动return [ // 默认缓存驱动 default env(cache.driver, redis), // 缓存连接配置 stores [ redis [ type redis, host env(redis.host, 127.0.0.1), port env(redis.port, 6379), password env(redis.password, ), // 无密码留空 select env(redis.select, 0), // 数据库索引0-15 timeout 0, // 超时时间 persistent false, // 是否长连接 prefix tp_seckill_, // 缓存前缀避免键名冲突 ], ], ];步骤 2环境变量配置.env 文件在项目根目录.env中添加 Redis 配置可选优先级更高REDIS_HOST127.0.0.1 REDIS_PORT6379 REDIS_PASSWORD REDIS_SELECT0步骤 3测试连接在 ThinkPHP 控制器中测试 Redis 是否可用namespace app\controller; use think\facade\Cache; class RedisTest { public function test() { // 写入缓存 Cache::set(test_key, hello redis, 3600); // 有效期1小时 // 读取缓存 $value Cache::get(test_key); echo $value; // 输出hello redis // 直接操作 Redis 原生方法获取 Redis 句柄 $redis Cache::store(redis)-handler(); $redis-set(native_key, 原生方法测试); echo $redis-get(native_key); // 输出原生方法测试 } }二、Redis 核心数据结构ThinkPHP 用法Redis 5 种核心数据结构对应 ThinkPHP 缓存操作重点掌握秒杀常用的StringHashList。2.1 String字符串- 秒杀库存存储用途存储商品库存、用户 token、计数器等ThinkPHP 操作示例// 1. 设置值库存初始化商品ID1001库存100 Cache::set(seckill_stock_1001, 100, 86400); // 2. 读取值 $stock Cache::get(seckill_stock_1001); // 100 // 3. 原子自减核心秒杀扣库存天然原子性 $remainStock Cache::decr(seckill_stock_1001); // 99返回减后的值 // 原子自增比如统计秒杀参与人数 Cache::incr(seckill_count_1001); // 1 // 4. 设置过期时间单独设置 Cache::expire(seckill_stock_1001, 3600); // 1小时后过期 // 5. 原生方法比如批量设置 $redis Cache::store(redis)-handler(); $redis-mset([ seckill_stock_1002 200, seckill_stock_1003 150 ]);2.2 Hash哈希- 存储商品详情、用户信息用途存储结构化数据比如商品信息避免多个 String 键ThinkPHP 操作示例// 1. 存储商品信息商品ID1001 Cache::hSet(seckill_goods_1001, name, iPhone 14); Cache::hSet(seckill_goods_1001, price, 5999); Cache::hSet(seckill_goods_1001, stock, 100); // 2. 读取单个字段 $price Cache::hGet(seckill_goods_1001, price); // 5999 // 3. 读取所有字段 $goodsInfo Cache::hGetAll(seckill_goods_1001); // 输出[name iPhone 14, price 5999, stock 100] // 4. 原子自减直接操作哈希中的库存字段 Cache::hDecr(seckill_goods_1001, stock); // 库存992.3 List列表- 异步队列、秒杀订单排队用途实现异步队列比如秒杀成功后异步同步订单到 MySQLThinkPHP 操作示例// 1. 入队秒杀成功后将订单信息加入队列 $orderInfo [ order_id uniqid(), goods_id 1001, user_id 10086, create_time time() ]; Cache::lpush(seckill_order_queue, json_encode($orderInfo)); // 左入队 // 2. 出队消费队列同步订单到 MySQL $orderJson Cache::rpop(seckill_order_queue); // 右出队FIFO队列 $order json_decode($orderJson, true); // 执行 MySQL 插入订单逻辑... // 3. 查看队列长度 $queueLen Cache::llen(seckill_order_queue); // 队列中的订单数2.4 Set集合- 去重、抽奖用途防止重复秒杀存储已秒杀用户 ID天然去重ThinkPHP 操作示例// 1. 添加用户到已秒杀集合用户ID10086商品ID1001 $isAdd Cache::sAdd(seckill_user_1001, 10086); // 返回1添加成功用户未秒杀过返回0添加失败用户已秒杀 // 2. 判断用户是否已秒杀 $hasSeckill Cache::sIsMember(seckill_user_1001, 10086); // true/false // 3. 获取已秒杀用户总数 $userCount Cache::sCard(seckill_user_1001); // 1 // 4. 移除用户退款场景 Cache::sRem(seckill_user_1001, 10086);2.5 ZSet有序集合- 排行榜用途秒杀销量排行榜、积分排名ThinkPHP 操作示例// 1. 添加商品销量到有序集合商品ID1001销量50 Cache::zAdd(seckill_sales_rank, 50, 1001); Cache::zAdd(seckill_sales_rank, 30, 1002); // 2. 按销量降序排列取前10名 $rank Cache::zRevRange(seckill_sales_rank, 0, 9, true); // 输出[1001 50, 1002 30]键商品ID值销量 // 3. 增加商品销量原子操作 Cache::zIncrBy(seckill_sales_rank, 1, 1001); // 商品1001销量变为51三、秒杀场景核心Redis 原子性与事务3.1 为什么秒杀必须保证原子性秒杀核心痛点高并发下超卖、库存不一致反例非原子操作会超卖// 错误代码先查库存再扣减两步非原子高并发下超卖 $stock Cache::get(seckill_stock_1001); if ($stock 0) { Cache::set(seckill_stock_1001, $stock - 1); // 高并发下多个请求同时执行导致库存为负 }核心解决方案用 Redis 原子命令或 Lua 脚本将 “查库存 扣库存” 封装为不可分割的操作3.2 Redis 原子性实现方式ThinkPHP 实战方式 1使用 Redis 原子命令推荐简单场景Redis 单个命令天然原子性比如decrhDecr直接用于扣库存// 秒杀扣库存核心代码原子操作无超卖 public function seckill($goodsId, $userId) { $stockKey seckill_stock_{$goodsId}; $userKey seckill_user_{$goodsId}; // 1. 先判断用户是否已秒杀Set去重 if (Cache::sIsMember($userKey, $userId)) { return [code 0, msg 已参与秒杀请勿重复提交]; } // 2. 原子扣减库存decr返回减后的值库存不足时返回-1 $remainStock Cache::decr($stockKey); if ($remainStock { return [code 0, msg 库存不足]; } // 3. 扣减成功添加用户到已秒杀集合 Cache::sAdd($userKey, $userId); // 4. 加入异步队列同步订单到MySQL $orderInfo [/* 订单数据 */]; Cache::lpush(seckill_order_queue, json_encode($orderInfo)); return [code 1, msg 秒杀成功]; }方式 2Lua 脚本复杂逻辑原子性推荐秒杀场景当需要 “判断库存 0 扣库存 记录用户” 多步逻辑时用 Lua 脚本保证原子性// ThinkPHP 中调用 Lua 脚本扣库存 public function seckillByLua($goodsId, $userId) { $redis Cache::store(redis)-handler(); $stockKey seckill_stock_{$goodsId}; $userKey seckill_user_{$goodsId}; // Lua 脚本判断库存扣库存记录用户原子操作 $lua stockKey KEYS[1] local userKey KEYS[2] local userId ARGV[1] -- 1. 判断用户是否已秒杀 if redis.call(sismember, userKey, userId) 1 then return 0 -- 已秒杀 end -- 2. 判断库存是否充足 local stock tonumber(redis.call(get, stockKey)) if not stock or stock return -1 -- 库存不足 end -- 3. 扣减库存 记录用户 redis.call(decr, stockKey) redis.call(sadd, userKey, userId) return 1 -- 秒杀成功 LUA; // 执行 Lua 脚本KEYS参数2个键ARGV参数用户ID $result $redis-eval($lua, [$stockKey, $userKey, $userId], 2); switch ($result) { case 1: // 加入订单队列... return [code 1, msg 秒杀成功]; case 0: return [code 0, msg 已参与秒杀]; case -1: return [code 0, msg 库存不足]; } }3.3 Redis 事务MULTI/EXEC- 了解即可Redis 事务不支持回滚适合无逻辑依赖的批量操作秒杀场景用得少$redis Cache::store(redis)-handler(); $redis-multi(); // 开启事务 $redis-decr(seckill_stock_1001); $redis-sAdd(seckill_user_1001, 10086); $redis-exec(); // 提交事务批量执行原子性3.4 Redis 三大特性在秒杀中的体现特性说明ThinkPHP 秒杀场景原子性用decr或 Lua 脚本保证 “扣库存 记录用户” 不可分割避免超卖一致性库存从 Redis 预扣减再异步同步到 MySQLRedis 拒绝非法操作比如对字符串库存 decr隔离性Redis 单线程执行命令秒杀高并发请求按顺序执行不会出现 “同时读库存、同时扣减” 的并发问题注意Redis 不保证持久性秒杀场景可接受因为 MySQL 是最终数据源Redis 宕机可从 MySQL 恢复库存四、ThinkPHP Redis 实战场景扩展4.1 缓存穿透解决方案秒杀场景恶意查询不存在的商品问题用户频繁查询不存在的商品 ID导致请求穿透 Redis 直接访问 MySQL解决方案缓存空值 布隆过滤器推荐// 缓存空值示例 public function getGoodsInfo($goodsId) { $cacheKey goods_info_{$goodsId}; $info Cache::get($cacheKey); if ($info false) { // 缓存未命中查询MySQL $info Db::name(goods)-find($goodsId); if (!$info) { // 存储空值设置短期过期比如10分钟 Cache::set($cacheKey, json_encode(null), 600); return [code 0, msg 商品不存在]; } // 缓存商品信息1小时过期 Cache::set($cacheKey, json_encode($info), 3600); } else { $info json_decode($info, true); if ($info null) { return [code 0, msg 商品不存在]; } } return [code 1, data $info]; }4.2 缓存击穿解决方案秒杀场景热点商品缓存过期问题秒杀热点商品的缓存过期瞬间大量请求直接打 MySQL解决方案互斥锁 缓存预热// 互斥锁示例ThinkPHP 结合 Redis setnx 实现 public function getHotGoodsInfo($goodsId) { $cacheKey hot_goods_{$goodsId}; $lockKey lock_hot_goods_{$goodsId}; $info Cache::get($cacheKey); if ($info) { return json_decode($info, true); } // 缓存未命中获取互斥锁 $lock Cache::setnx($lockKey, 1); if ($lock) { // 获得锁查询MySQL并更新缓存 $info Db::name(goods)-find($goodsId); Cache::set($cacheKey, json_encode($info), 3600); // 延长缓存时间 Cache::del($lockKey); // 释放锁 return $info;这里是不务正业的程序员

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询