公司动态
MySQL 底层 + 索引优化 + 分表 + Java 并发编程
MySQL 底层MySQL 索引底层 B 树、聚簇 / 二级索引、最左前缀、覆盖索引B 树核心设计MySQL 选用原因基础概念B 树是多路平衡搜索树InnoDB 默认用它做索引MySQL 最大瓶颈是磁盘 IOB 树所有设计都是为了减少磁盘读取次数。三大核心特性非叶子节点只存索引键 主键不存完整数据一页 16KB 能存放更多索引树更矮IO 更少只有叶子节点保存真实数据叶子节点通过双向链表串联有序范围查询、分页不用多次跳转节点。对比表格B 树 VS B 树 VS Hash 索引索引结构优势劣势是否适合短信日志B 树范围查询快、排序、分页友好、IO 少等值查询略慢于 Hash✅ 适合按手机号、时间查日志B 树等值查询还行数据分散所有节点范围查询大量 IO❌ 不适合Hash 索引等值匹配极快不支持范围、排序、前缀匹配哈希冲突❌ 不适合日志检索生活化举例把 B 树想象成字典非叶子节点 字典目录只记录关键字和页码不存全文叶子节点 字典正文完整内容叶子双向链表 正文按顺序装订想查 8 月 1 日8 月 10 日日志直接向后翻页不用反复跳目录。聚簇索引 二级索引聚簇索引InnoDB 每张表必须有且仅有 1 个强制存在如果你手动设置主键 → 主键直接作为聚簇索引没主键但存在唯一非空字段→ 自动选这个唯一索引当聚簇既无主键、也无唯一非空索引 → InnoDB 内部自动生成隐藏列row_id6 字节用它构建聚簇索引。简单说不管你建不建底层一定存在聚簇索引表的数据本身就是依附聚簇索引的 B 树存储的。聚簇索引特点一张表只能有 1 个默认主键就是聚簇索引无主键自动生成隐藏 rowidB 树叶子节点存储整条完整数据数据物理磁盘顺序和主键排序一致。举例短信日志表主键 id1、2、3磁盘上数据就是按 1、2、3 连续存放。二级索引普通 / 联合索引完全可选可 0 个、可多个一张表可以一个二级索引都不创建只保留聚簇索引 场景比如只做主键单条查询、无其他查询条件不需要额外建索引。也可以根据业务创建几十上百个单列 / 联合二级索引没有数量强制限制。叶子统一格式索引全部字段 主键单列索引 idx_phone (phone)叶子存储 phone 主键 id联合索引 idx_phone_time (phone,create_time)叶子存储 phone,create_time 主键 id。回表流程SQL 走二级索引匹配条件拿到主键拿着主键去聚簇索引 B 树查询完整行数据多一次磁盘 IO性能损耗优化目标尽量避免回表。覆盖索引消除回表核心方案满足条件SQL 中 select 查询字段、where 过滤字段全部存在二级索引字段内。 此时直接从二级索引拿全部数据无需访问聚簇索引执行计划 Extra 显示Using index。示例 索引 idx_phone_time (phone,create_time)sqlselect phone,create_time from sms_log where phone13800000000;查询字段 phone、create_time 都在索引中命中覆盖索引不回表。联合索引最左前缀原则核心规则联合索引字段顺序固定idx (a,b,c) 固定 a→b→c能否使用该索引看 WHERE 条件不是 SELECT 字段。WHERE 全等值条件书写顺序无关MySQL 优化器自动调换匹配索引最左字段缺少索引最左侧字段无法使用该联合索引索引顺序中遇到范围条件 ( between)范围后面所有索引字段失效。案例表格索引 idx (a,b,c)SQL 条件可走索引字段说明a1 and b2 and c3a、b、c全部等值全部生效a1 and b2 and c3a、bb 是范围后面 c 失效b2 and a1a、b等值自动调换顺序正常使用b2 and c3无缺少最左字段 a索引用不上生活化举例联合索引 (a,b,c) 如同字典目录一级分类 a、二级分类 b、三级分类 c。只知道二级分类 b不知道一级分类 a无法快速定位目录找到 a 之后b 用范围筛选三级分类 c 顺序打乱无法依靠目录快速查找。索引失效高频场景索引列使用函数、隐式类型转换like % 关键词 左模糊匹配or 两侧存在无索引字段联合索引不满足最左前缀不走索引SQL 会降级为全表扫描 typeALL。巩固题目题目InnoDB 表主键为 id创建联合索引idx_phone_ct(phone, create_time)执行 SQLselect create_time from sms_log where create_time 2026-08-01这条 SQL 能否使用idx_phone_ct索引说明原因。结论无法使用idx_phone_ct(phone, create_time)原理联合索引最左前缀规则索引第一位是 phonewhere 条件里完全没出现 phone直接跳过最左侧字段该联合索引失效补充哪怕 select 只查 create_time也救不了 where 缺少最左字段的问题能不能走索引只看过滤条件和查询字段无关。表主键 id索引idx(a,b,c)SQLwhere a1 and c5 and b 10问题a、b、c 三个字段中哪些可以用到索引哪些失效说明理由。a、b 可以走索引c 失效完整理由索引固定顺序(a,b,c)SQL 条件a1 and c5 and b10a 是等值最左字段必然先走索引优化器自动整理为a1 and b10 and c5b 是范围条件能走索引c 出现在范围 b 的后方索引有序性断裂c 无法利用索引过滤只能内存过滤。MVCC 多版本并发控制核心作用结合短信业务举例运营后台几十个人同时查短信日志大量读同时业务不停新增、修改短信下发记录写操作。如果没有 MVCC读操作会加锁阻塞写入系统卡顿 MVCC 让读不用加锁读写互不阻塞大幅提升并发能力。MVCC 三大核心组件1.undo log回滚日志数据每次修改前会把修改前的旧数据复制一份存进 undo log形成一条版本链。生活化举例修改短信状态前先把 “待发送” 旧状态存档后续别的事务读快照时就调取这份存档旧数据。2.行隐藏列每条记录自带看不见DB_TRX_ID最后修改这条数据的事务 IDDB_ROLL_PTR指针指向 undo log 里这条数据的上一个旧版本串联成版本链表。3.Read View读视图快照读执行时生成的视图里面记录了当前活跃的所有事务 ID用来判断这条数据的版本对当前事务是否可见。快照读 VS 当前读 对比表类型对应 SQL是否加锁读取数据适用场景快照读普通 select不加任何行锁历史快照数据后台查询短信日志、报表统计当前读insert/update/delete/select ... for update加行锁数据库最新数据修改短信状态、扣短信额度、订单更新InnoDB 四大事务隔离级别隔离级别解决 3 个并发问题脏读、不可重复读、幻读级别越高并发性能越差锁越严格。 从低到高排序读未提交Read UncommittedRU读已提交Read CommittedRC可重复读Repeatable ReadRRMySQL InnoDB 默认级别串行化SerializableSERIALIZABLE先搞懂 3 个并发异常假设场景事务 A 查询 13800 短信发送记录事务 B 修改 / 新增这条短信数据。1.脏读事务 B 修改短信状态还没提交事务 A 读到了 B 未提交的数据。 一旦 B 回滚A 读到的数据就是无效脏数据。2.不可重复读同一个事务 A先后两次查询同一条短信中间事务 B 更新并提交。 两次查询结果不一样数据重复读取不一致。3.幻读同一个事务 A两次按条件批量查短信列表中间事务 B 插入新短信并提交。 第二次查询多出一行数据像凭空出现如同幻觉。四大隔离级别完整对比表隔离级别脏读不可重复读幻读MVCC 是否启用锁机制特点读未提交 RU✅ 允许✅ 允许✅ 允许不使用写不加锁读不加锁完全无隔离生产不用读已提交 RC❌ 禁止✅ 允许✅ 允许使用 MVCC每次快照读生成新 Read View无间隙锁可重复读 RR默认❌ 禁止❌ 禁止❌ 禁止使用 MVCC 间隙锁事务只生成 1 次 Read View行锁 间隙锁解决幻读串行化 SERIALIZABLE❌ 禁止❌ 禁止❌ 禁止不使用 MVCC普通 select 自动加共享锁读写完全互斥并发极低InnoDB 默认隔离级别 RR可重复读MVCC 解决不可重复读同一个事务多次快照读看到的数据始终一致单纯 MVCC 无法解决幻读RR 级别依靠间隙锁Gap Lock配合 MVCC彻底解决幻读。通俗解释幻读事务 A 查询手机号 138xxx 的短信只有 2 条此时事务 B 插入一条同手机号短信并提交A 再次查询多出 1 条像幻觉一样这就是幻读。RR 靠间隙锁锁住数据中间的空白区间禁止其他事务插入避免幻读。版本可见性逻辑极简理解执行快照读生成 Read View按规则判断这条记录能不能看见数据修改事务 ID 当前视图最小活跃事务 ID → 可见数据修改事务 ID 在活跃事务区间内 → 不可见顺着 roll_ptr 去 undo log 找旧版本数据修改事务 ID 当前视图最大活跃事务 ID → 不可见。巩固题目题目MySQL InnoDB 默认隔离级别是什么该隔离级别依靠什么机制分别解决不可重复读、幻读InnoDB 默认事务隔离级别可重复读RR解决不可重复读依靠 MVCC 机制事务第一次快照读时生成 1 份 Read View整个事务全程复用该视图多次查询数据保持一致解决幻读MVCC 无法单独解决幻读RR 隔离级别通过临键锁行锁 间隙锁配合 MVCC锁住记录间隙阻止插入消除幻读。题目区分快照读和当前读分别写出对应的 SQL说明两者是否加锁、读取的数据版本。快照读对应 SQL普通select 字段 from 表 where 条件;加锁情况不加任何行锁读取数据事务快照里的历史旧数据看不到其他事务未提交的修改当前读对应 SQLinsert、update、delete、select ... for update、select ... lock in share mode加锁情况添加行锁排他锁 / 共享锁读取数据数据库最新已提交的真实数据InnoDB 锁机制行锁、间隙锁、临键锁锁的基础分类行锁Record Lock作用精准锁定索引上存在的某一条真实记录生效前提SQL 的 where 条件能精准匹配到索引精准定位单条 / 多条记录锁类型共享锁 S、排他锁 X。生活化举例短信表有 phone 索引更新单个手机号记录只锁这一条短信数据别的手机号数据可正常修改互不干扰。间隙锁Gap Lock仅在 RR可重复读隔离级别存在作用锁定两条记录中间的空白区间阻止其他事务在这个间隙插入新数据用来解决幻读不锁定记录本身只锁空隙。举例表里手机号索引值 138001、138003间隙锁锁住(138001,138003)别人无法插入 138002 这条数据。临键锁 Next-Key LockRR 默认加锁算法临键锁 行锁 间隙锁锁定一段左开右闭区间(前一条数据, 当前数据]既能锁住当前真实存在的记录又锁住记录前面的间隙防止插入是 RR 级别解决幻读的核心锁。三种锁对比表格锁类型锁定对象存在隔离级别核心作用行锁 Record已存在的单条索引记录RR/RC 都有阻止别人修改、删除本条数据间隙锁 Gap记录之间的空白区间仅 RR阻止插入新数据防幻读临键锁 Next-Key间隙 当前记录仅 RR行锁 间隙锁一体RR 默认锁行锁变表锁致命坑触发条件更新 / 删除语句 where 条件无索引全表扫描加行锁。危害整张表被锁住所有写入操作阻塞短信批量下发、状态更新全部卡死。优化方案业务过滤字段必须建立索引避免无索引更新。共享锁 S、排他锁 X 兼容表已有锁再加共享锁 S再加排他锁 X共享锁 S兼容可同时加互斥阻塞等待排他锁 X互斥阻塞等待互斥阻塞等待通俗解释多条事务同时查询加共享锁互不阻塞只要有一个事务加了排他锁修改数据其他任何读写锁都会被堵住。短信业务冲突场景举例事务 A 执行select * from sms_log where phone13800000000 for update;加排他行锁此时其他事务不能修改、删除这条手机号短信不能插入这个手机号区间的新短信间隙锁限制其他手机号短信不受影响可正常读写。巩固题目题目简述行锁什么时候会降级成全表表锁举短信业务场景例子。核心原因执行update/delete等当前读语句时where查询条件无法命中索引InnoDB 无法精准锁定单行记录只能扫描全表所有行给每一行加行锁效果等同于锁住整张表俗称降级表锁。短信业务举例短信日志表sms_log字段phone未建立索引执行语句update sms_log set status1 where phone13800000000;where 条件 phone 无索引数据库遍历全表每条记录加行锁整张表被锁定其他所有写入操作阻塞等价表锁。题目RR 隔离级别下InnoDB 默认的加锁算法是什么由哪两种锁组合而成各自作用是什么默认加锁算法临键锁Next-Key Lock组成行锁Record Lock 间隙锁Gap Lock各自作用行锁锁定索引上真实存在的记录阻止其他事务更新、删除这条数据间隙锁锁定索引记录之间的空白间隙阻止其他事务插入新数据解决幻读。题目执行 update 语句时什么情况下行锁会退化成表锁举短信业务 SQL 示例。触发条件update/delete的 where 条件字段无索引无法走索引会全表扫描并给所有行加行锁表现等同于表锁。 短信业务示例-- phone字段未建索引update sms_log set status2 where phone13800000000;该 SQL 会遍历全表所有记录加行锁其他所有新增、更新短信的操作全部阻塞。慢 SQL 优化 Explain 执行计划慢 SQL 完整优化步骤结合短信日志业务1.抓取慢 SQL开启 MySQL 慢查询日志设置 long_query_time 阈值比如超过 1 秒记录自动记录执行慢的 SQL短信平台里大多是短信日志查询、批量统计类 SQL。2.Explain 分析执行计划重点看 type、key、Extra 三个字段定位性能瓶颈全表扫描、索引失效、Using filesort、Using temporary、大量回表。3.针对性优化缺索引建立联合索引、覆盖索引索引失效避免索引列函数、左模糊、不满足最左前缀分页深翻页优化 limit 分页多表关联关联字段加索引减少笛卡尔积。4.验证优化效果再次 Explain 看执行计划是否变好本地压测对比执行耗时。5.上线落地灰度发布观察线上慢查询日志确认无性能退化。Explain 核心字段详解1.type访问类型性能从优到劣排序system const eq_ref ref range index ALLALL全表扫描性能最差必须优化range走索引范围查询 between短信按时间查日志常用ref等值匹配普通索引const主键 / 唯一索引等值匹配单行数据性能最优。2. key实际执行中真正用到的索引NULL 代表没使用任何索引发生全表扫描。3. Extra额外执行信息重点记 3 个标识标识含义好坏Using index命中覆盖索引无需回表优秀Using filesort无法利用索引排序内存不够会磁盘文件排序危险需要优化Using temporary创建临时表存储中间结果多出现于 group by/distinct危险需要优化短信日志查询 SQLselect phone,create_time from sms_log where phone13800000000 order by create_time;建立联合索引idx_phone_ct(phone,create_time)type 为 refkey 使用 idx_phone_ctExtra 出现 Using index无需回表、无需文件排序性能最优。常见索引失效场景汇总索引列参与函数运算、隐式类型转换 例where substr(phone,1,3)138、字符串数字不加引号匹配数字字段like 左模糊like %13800索引失效like 13800%可以走索引or 左右两边一侧字段无索引整体索引失效联合索引不满足最左前缀原则where 条件无索引更新语句行锁变表锁。短信业务优化实例千万级 sms_log 表查询语句select * from sms_log where create_time 2026-08-01 and phone13800000000 order by create_time;原始只建单字段索引 phone每次查询需要回表 文件排序优化方案创建联合覆盖索引idx_phone_ct(phone,create_time)查询只查 phone、create_time消除回表与 filesort。巩固题目题目简述完整慢 SQL 优化流程。完整慢 SQL 优化通用步骤开启 slow_query_log 慢查询日志抓取线上执行耗时过长的慢 SQL使用 Explain 分析慢 SQL 执行计划定位问题是否全表扫描、索引失效、大量回表、文件排序、临时表等根据问题针对性优化建立合适联合索引、改写 SQL、使用覆盖索引减少回表、优化分页逻辑等优化完成后再次执行 Explain 验证执行计划改善线上压测对比前后耗时确认性能提升后上线。题目Explain 中 type 字段性能优先级从优到劣完整排序并说明 ALL 和 range 分别代表什么执行方式。type 性能从优到劣顺序system const eq_ref ref range index ALL字段解释ALL全表扫描完全不使用索引性能最差必须优化range基于索引做范围查询匹配条件包含 、、、、between、in 等短信按时间段查日志就是典型场景ref普通索引等值匹配可匹配多条数据const主键 / 唯一索引等值精准匹配仅返回单行性能最优。题目Explain 的 Extra 中Using index、Using filesort、Using temporary 分别代表什么哪些是需要优化的危险标识Using index命中覆盖索引查询所需字段全部存在二级索引内无需回表查询聚簇索引属于性能优秀标识Using filesort无法依靠索引完成排序MySQL 会额外执行排序操作内存不足时会落地磁盘文件排序消耗大量 IO属于危险标识必须优化Using temporary执行过程中创建临时表存放中间数据常见于 group by、distinct、多表联查场景开销大属于危险标识必须优化。千万级短信日志分表方案业务背景贴合智能营销短信平台sms_log短信下发日志每日大量产生单表千万级之后索引变大、查询变慢、备份维护压力大所以必须分表。业务特点绝大多数查询都会带create_time时间条件查某天 / 某月发送记录极少无时间全量检索。两种分表方案详细对比表方案路由规则优点缺点适合场景时间范围分表create_time 按月 / 按天分表表名sms_log_202608按时间查询只访问少量分表过期数据直接 drop 表清理归档极简当前周期表是写入热点不带时间条件会扫全部分表✅ 短信日志、操作日志、审计日志我们项目选用Hash 取模分表对 phone / 主键 id hash 后 % N 分到 N 张表数据均匀分散写入压力均衡无热点表扩容很难扩表需要数据迁移按时间查询要扫所有分表用户订单、以用户 ID 为主查询的业务时间分表落地细节分表维度短信日志一般按月分表数据量超大时按天分表路由逻辑代码层根据传入的 create_time自动拼接表名sms_log_202608自动建表定时任务提前预创建下个月的分表避免写入时报表不存在过期清理保留 6 个月日志到期直接 drop 旧分表比 delete 删除高效得多delete 会产生大量 undo、碎片。时间分表最大坑重点记忆如果业务 SQL不带时间条件框架不知道去哪张表查只能遍历所有分表性能暴跌。业务约束强制所有短信日志查询必须携带时间区间参数。和哈希分表对比一句话总结短信日志天然按时间检索优先时间范围分表如果是用户交易类、主要按用户 ID 查询、查询很少按时间范围才考虑 hash 取模分表。巩固题目题目短信日志分表有两种主流方案按时间范围分表、哈希取模分表分别说下各自优缺点项目里我们选择哪一种按时间范围分表按月 / 按天分表项目选用这个优点短信日志查询基本都会携带时间范围条件可以直接路由到对应月份 / 日期的分表跨表少归档、清理过期数据直接 drop 旧表维护简单。缺点容易出现热点表当下这个月的表持续写入压力集中如果查询不带时间条件需要扫描所有分表。哈希取模分表按手机号 / 主键 hash 取模优点数据均匀打散写入压力均衡无热点表。缺点扩容困难增加分表数量时大部分数据要重新迁移短信日志经常按时间查会遍历所有分表性能差。项目选型短信日志选用按时间范围分表贴合业务查询习惯归档清理方便。题目短信日志采用按月时间范围分表说出该方案的核心优点和最大风险点。按月时间范围分表优点短信日志大多携带时间条件查询可以直接路由到对应月份的少数分表查询效率高过期归档数据可以直接drop表相比 delete 清理无日志碎片、清理成本极低。风险 / 缺点① 存在热点表问题当前活跃月份持续写入压力集中数据分布不均衡② 如果查询 SQL 不带时间范围条件程序需要遍历所有分表检索性能会急剧下降。题目如果短信业务核心查询是根据手机号查询下发记录很少按时间段批量查日志此时优先选哪种分表方案理由是什么优先选用哈希取模分表以手机号做 hash 取模理由以手机号作为核心查询维度hash 取模可以把数据均匀打散到各个分表写入压力均衡不存在热点表根据手机号查询时直接通过 hash 路由定位到单张分表查询效率高。缺点补充后续扩容比较麻烦新增分表数量时大部分数据需要迁移如果要按时间批量查询则需要遍历所有分表。Java 并发HashMap 线程池 volatile/synchronized线程池七大核心参数结合批量短信下发public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)逐个通俗解释结合批量短信推送业务corePoolSize 核心线程数常驻存活的线程就算空闲也不会回收专门稳定处理短信下发任务maximumPoolSize 最大线程数核心线程忙满之后最多可以扩容到的线程总数控制整体上限防止无限创建线程 OOMkeepAliveTime unit 空闲超时时间非核心线程空闲多久之后自动回收节省资源workQueue 阻塞队列核心线程全部繁忙后新的短信下发任务先放进队列排队threadFactory 线程工厂自定义线程名称方便线上日志排查例如batch-sms-thread-xxhandler 拒绝策略队列满、线程数也达到最大值新来任务触发拒绝策略。结合项目批量短信任务线程池选型批量营销短信推送属于 IO 密集型大部分时间在调用短信通道、MQ、RedisCPU 等待IO 密集型经验公式核心线程数 ≈ 2 * CPU 核心数 1拒绝策略自定义拒绝策略任务拒绝时记录日志、告警、消息落盘不要直接抛异常丢失短信任务队列不要用无界队列无界队列会堆积大量任务内存持续上涨引发 OOM。4 种内置拒绝策略AbortPolicy默认直接抛 RejectedExecutionException业务容易丢消息短信业务不能用DiscardPolicy默默丢弃任务不抛异常短信绝对不能用会丢下发任务DiscardOldestPolicy丢弃队列最老任务再尝试提交新任务也不适合短信CallerRunsPolicy交给调用者线程自己执行起到简单削峰适合部分场景但短信批量任务优先自定义拒绝策略做告警 保存待重试HashMap 底层JDK1.8 重点底层结构数组 链表 红黑树阈值链表长度≥8转红黑树红黑树节点≤6退化为链表寻址key 做 hash 扰动 → 取模数组长度定位桶位扩容负载因子默认 0.75达到阈值自动扩容为原来 2 倍迁移数据区分HashMap 线程不安全并发场景优先 ConcurrentHashMapvolatile 和 synchronizedvolatile作用保证可见性、禁止指令重排不保证原子性适用场景标记位、状态开关比如批量短信任务启停标识不能用来做计数累加这类原子操作synchronized作用保证可见性、原子性、有序性分类对象锁、类锁偏向锁→轻量级锁→重量级锁升级JDK1.6 之后适合短时间临界区同步比如简单共享变量修改一句话区分volatile 适合状态标记轻量无锁synchronized 是互斥锁保证原子性适合竞争修改共享数据。并发容器选型结合短信业务HashMap单线程使用并发不安全ConcurrentHashMap高并发读写缓存、存储临时短信元数据推荐ArrayBlockingQueue / LinkedBlockingQueue线程池任务队列、生产者消费者场景巩固题目题目结合批量营销短信下发场景简述为什么不直接new Thread()创建线程而是要用线程池在批量营销短信下发这种高并发场景不直接 new Thread优先使用线程池原因频繁 new Thread、销毁线程开销很大线程属于操作系统资源创建销毁会消耗 CPU无限制创建线程并发量大时线程数量持续上涨抢占内存极易引发 OOM线程池可以统一管控线程数量、任务队列、拒绝策略方便限流、监控、复用线程方便统一管理批量短信任务支持设置任务拒绝策略、超时监控适配短信下发业务削峰。题目批量营销短信下发属于 IO 密集型任务讲下 IO 密集型线程池核心线程数经验公式、队列选型禁忌以及短信业务拒绝策略为什么不推荐使用默认 AbortPolicyIO 密集型线程池经验公式核心线程数 2 * CPU 核心数 1队列禁忌禁止使用无界阻塞队列任务持续堆积会不断占用内存引发 OOM不推荐 AbortPolicy 原因AbortPolicy 是默认策略队列满、线程打满时直接抛出异常。短信下发任务不能随意丢失直接抛异常会导致本次待下发的短信任务丢失业务受损所以不采用优先自定义拒绝策略实现告警 消息持久化重试。题目volatile 关键字能保证什么不能保证什么结合批量短信任务举一个合适的使用场景。volatile 可以保证内存可见性、禁止指令重排序无法保证原子性业务场景批量短信推送任务里可以用 volatile 修饰任务启停开关标识主线程修改开关状态工作线程能立刻感知到启停信号及时停止拉取新的短信下发任务。补充注意不能拿 volatile 做短信计数累加这类操作因为不具备原子性。