ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Hive调优实战:Tez与MapReduce引擎差异及核心参数优化指南

Hive调优实战:Tez与MapReduce引擎差异及核心参数优化指南 1. 项目概述为什么Hive调优不是“选个参数改一改”就完事了Hive 基于常用参数的调优Tez和MR引擎——这个标题里藏着一个被太多人低估的真相它根本不是一份“参数清单CtrlC/V”的速查手册而是一套需要你同时理解SQL语义、执行引擎行为、YARN资源调度逻辑、数据物理分布特征四层结构的系统性工程。我带过三届某高校大数据实验室的实训项目每年都有至少三分之一的学生卡在同一个地方把hive.exec.reducers.bytes.per.reducer256000000改成128000000后作业反而从3分钟拖到12分钟或者盲目调大tez.grouping.min-size结果小文件爆炸式增长NameNode内存直接告警。问题出在哪不是参数错了是他们只看见了“参数”没看见参数背后那个正在奔跑的执行计划。Tez和MR表面都是执行引擎但内核逻辑天差地别MR是严格的两阶段Map→Shuffle→Reduce所有中间结果必须落盘Tez则是DAG驱动允许Map-Reduce-Map链式复用甚至能把Join和Filter合并进一个顶点。这意味着同样一条SELECT COUNT(*) FROM sales WHERE dt2024-03-15 AND region华东在MR下可能触发全表扫描两次Shuffle在Tez下却能通过Vertex重用动态分区裁剪把I/O压缩掉70%。所以调优的第一步永远不是打开hive-site.xml而是先用EXPLAIN看懂你的SQL到底被翻译成了什么样子。这不是玄学是每个真实生产环境里每天都在发生的决策现场某公司电商中台团队曾因未识别出一个隐式类型转换string字段和int常量比较导致整个订单宽表Join失效被迫重跑TB级任务损失近4小时计算资源。你调的不是参数是你对数据流动路径的掌控力。2. 执行引擎底层逻辑拆解Tez与MR的基因差异决定调优方向2.1 MR引擎稳定但笨重的“流水线工人”MapReduce的本质是把计算任务强行切分成两个不可逾越的阶段Map负责局部处理Reduce负责全局聚合。这种设计带来了极强的容错性——任何一个Map Task失败只需重跑该分片但代价是强制落盘。每一轮Shuffle数据必须从Map端写入本地磁盘再由Reduce端拉取、排序、归并。这就决定了MR调优的核心矛盾如何在磁盘I/O、网络传输、内存占用三者间找平衡点。比如mapreduce.map.memory.mb设得太低JVM频繁GCMap Task启动慢设得太高YARN容器分配不均集群并发度骤降。更隐蔽的是mapreduce.input.fileinputformat.split.minsize这个参数——它控制InputSplit最小大小直接影响Map Task数量。假设你有一批平均200MB的ORC文件若split.minsize128MB每个文件会生成1~2个SplitTask数可控但若误设为10MB200MB文件会被切成20个Split瞬间多出19个Map Task每个Task只处理10MB数据大量时间耗在JVM启动和心跳通信上实际吞吐反而暴跌。我实测过某日志分析场景将split.minsize从默认的1B即按文件块切调整为256MBMap Task数从1280个降到320个总耗时从8分23秒缩短至4分17秒CPU利用率从35%升至68%因为减少了75%的进程调度开销。这说明MR调优的关键不是“堆资源”而是让每个Task承载合理的工作负载避免“大马拉小车”或“小马拉大车”。2.2 Tez引擎灵活但敏感的“交响乐团指挥”Tez抛弃了MR的僵化两阶段用DAG有向无环图描述任务依赖。一个典型的Join操作在Tez中可能被编译成三个顶点V1读取左表过滤V2读取右表过滤V3执行Hash Join。这三个顶点可以并行启动V1/V2的输出直接通过内存管道Tez Runtime传给V3完全绕过磁盘。这种设计释放了巨大性能潜力但也引入了新挑战顶点间的资源竞争、内存缓冲区溢出、动态规划失败。比如tez.runtime.io.sort.mb它定义了每个Task用于排序的内存缓冲区大小。设得太小如64MB数据频繁spill到磁盘失去Tez优势设得太大如2GB单个Task可能吃光NodeManager内存触发YARN Kill。更关键的是tez.grouping.min-size——它控制输入分组的最小字节数直接影响顶点并行度。假设你有1000个小于10MB的Parquet文件若min-size1MBTez可能为每个文件创建一个Task产生1000个顶点若设为100MB它会尝试将10个文件合并成一个InputGroupTask数降到100个。但这里有个陷阱如果文件大小极度不均90%是5MB10%是500MBmin-size100MB会导致大文件单独占一个Task小文件却要凑够100MB才启动造成严重的长尾效应。我们曾在线上遇到过一个ETL任务因tez.grouping.min-size设置不当95%的Task在2分钟内完成最后5个Task卡在30分钟以上根源就是3个500MB文件被单独调度而其他小文件还在等待凑组。Tez调优的精髓在于用参数引导引擎做更聪明的决策而不是替它做决策。2.3 引擎切换不是开关而是重构执行路径很多人以为把hive.execution.enginemr改成tez就能自动加速这是最大误区。Hive的SQL优化器Calcite会为不同引擎生成不同执行计划。比如ORDER BY在MR下必须走全局Reduce排序而在Tez下可能被优化为TopN顶点局部排序大幅减少数据移动。但这种优化有前提你的数据必须满足某些条件。例如SELECT * FROM user_log ORDER BY ts DESC LIMIT 100如果user_log表按ts字段做了Bucketing且数据已排序Tez可能直接读取最新Bucket的头部数据但如果表是随机插入的Tez仍需全表扫描排序。因此引擎切换必须配合表结构改造对高频排序字段建Bucket对Join键做Sort Merge Bucket Join对小表启用hive.auto.convert.jointrue触发Map Join。某金融风控团队迁移Tez时仅改引擎未动表结构QPS提升不足15%加入Bucket优化后核心反欺诈查询响应时间从2.3秒降至0.4秒。这印证了一个事实引擎是肌肉表结构是骨骼没有匹配的骨骼再强的肌肉也发挥不出力量。3. 核心参数详解与实操配置从原理到落地的完整闭环3.1 内存类参数不是越大越好而是精准匹配数据特征内存配置是调优的基石但错误配置比不配更危险。以hive.tez.container.size为例它定义了Tez Task容器的总内存上限。很多团队直接设为NodeManager可用内存的80%结果发现大量Task因OOM被Kill。原因在于container.size包含JVM堆内存-Xmx和堆外内存Direct Memory、Code Cache等。若container.size4GJVM堆通常只能分到3G剩下1G给堆外使用。当tez.runtime.io.sort.mb20482G排序缓冲区时堆外内存已接近极限一旦发生GC或网络抖动极易溢出。正确做法是建立计算公式container.size ≈ (tez.runtime.io.sort.mb tez.runtime.unordered.output.buffer.size.mb JVM Overhead) × 1.2其中JVM Overhead按堆内存的25%估算。例如目标排序缓冲区1.5G则container.size ≈ (1536 256 1536×0.25) × 1.2 ≈ (1536 256 384) × 1.2 2176 × 1.2 ≈ 2611MB → 取整为2816MB2.75G这样既留出安全余量又避免资源浪费。我在某广告平台实测将container.size从4G降至2.75Gtez.runtime.io.sort.mb同步从2G调至1.5GTask失败率从8.7%降至0.3%平均执行时间缩短19%。因为更小的容器能提高YARN调度并发度而1.5G排序缓冲区足以覆盖95%的中间数据规模避免了频繁spill。3.2 并行度参数控制“多少人干活”更要控制“每人干多少”并行度参数直接决定集群资源利用率。hive.exec.reducers.bytes.per.reducerMR和tez.grouping.min-sizeTez看似功能相似实则逻辑相反前者是上限控制每个Reducer最多处理多少字节后者是下限控制每个InputGroup至少多少字节。这导致调优策略截然不同。对于MR计算理想Reducer数的公式是reducers_num min( max_reducers, ceil( input_data_size / bytes_per_reducer ) )其中max_reducers由hive.exec.reducers.max限定。假设处理1TB数据bytes_per_reducer256MB则理论Reducer数为1024GB / 0.25GB 4096。但若max_reducers1000最终只有1000个Reducer每个需处理1GB数据容易OOM。此时应优先调大max_reducers而非盲目减小bytes_per_reducer。对于Teztez.grouping.min-size的设定需结合文件统计信息。我们开发了一个小脚本扫描HDFS目录输出文件大小分布hdfs dfs -ls /data/sales/* | awk {print $5} | sort -n | awk BEGIN{count0;sum0;min999999999;max0} {count; sum$1; if($1min)min$1; if($1max)max$1} END{printf Count:%d, Avg:%.0fMB, Min:%.0fKB, Max:%.0fGB\n, count, sum/1024/1024, min/1024, max/1024/1024/1024}实测某电商日志表12800个文件平均大小85MB最小4KB最大3.2GB。若设min-size128MB约60%文件需合并Task数预估为5120若设min-size64MBTask数升至8192。经压测5120个Task时YARN队列等待时间最短平均1.2秒而8192个Task时等待飙升至8.7秒。结论选择min-size应使Task数落在集群并发能力的“甜蜜点”通常为集群总vCore数的1.5~2倍。3.3 数据读写类参数让IO成为加速器而非瓶颈Hive的IO性能极大依赖底层文件格式和读写参数。ORC/Parquet是标配但参数配置常被忽视。hive.exec.orc.split.strategyBI默认适合大宽表它按Stripe切分而hive.exec.orc.split.strategyHYBRID会智能判断对小文件用ETL策略按文件切避免过度切分。某用户行为分析任务原始用BI策略1000个10MB ORC文件生成10000个SplitTask数爆炸切换HYBRID后Split数降至1200耗时下降41%。另一个关键是orc.compress和orc.stripe.size。orc.compressZLIB压缩率高但CPU贵SNAPPY则更快。我们做过对比处理100GB原始日志ZLIB压缩后22GBSNAPPY压缩后38GB但SNAPPY解压速度是ZLIB的3.2倍。在CPU充足的集群选ZLIB在IO密集型任务如报表导出选SNAPPY更优。orc.stripe.size控制Stripe大小默认256MB。Stripe是ORC的读取单元设得太小如64MB增加元数据开销太大如1GB导致单次读取数据过多缓存命中率下降。经验法则是stripe.size ≈ 4 × avg_query_result_size。例如多数查询返回10MB结果集则stripe.size40MB较合适。3.4 Join与聚合优化参数从算法层面降维打击Join和聚合是Hive最耗时的操作参数调优直击算法核心。hive.auto.convert.jointrue开启Map Join将小表广播到每个Map Task内存中彻底规避Shuffle。但阈值hive.mapjoin.smalltable.filesize默认25MB常被忽略。某团队有张维度表32MB因超阈值无法触发Map Join被迫走Reduce Join耗时增加5倍。解决方案不是硬调阈值而是用ALTER TABLE dim_user SET TBLPROPERTIES(auto.purgetrue)清理历史版本并用INSERT OVERWRITE重建表利用ORC的轻量压缩将体积压到22MB。聚合方面hive.groupby.skewindatatrue是处理数据倾斜的利器。它启用两阶段聚合第一阶段加随机前缀打散Key第二阶段去前缀汇总。但副作用是增加一次Shuffle。因此仅在EXPLAIN确认存在严重倾斜如某个Reducer处理数据量超均值10倍时启用。我们曾监控到一个订单表GroupByprovince北京、广东数据量是其他省的50倍开启此参数后长尾Reducer消失整体耗时下降63%。4. 实操全流程从诊断到上线的七步工作法4.1 第一步基线捕获与问题定位30分钟任何调优前必须建立可量化的基线。我坚持用三组数据锁定问题执行计划快照EXPLAIN EXTENDED SELECT ...重点关注Stage-1的Map Reduce或Tez标识、Edges数量、Vertices依赖关系。特别留意FileScan是否走了分区裁剪Partition Filters、Join是否标记Map Join。资源消耗日志在YARN UI中找到对应Application下载logs/stdout搜索GC time、Spill、Container Killed等关键词。一次典型失败日志显示2024-03-15 10:23:41,221 INFO [IPC Server handler 10 on 41234] org.apache.hadoop.mapred.TaskAttemptListenerImpl: Task attempt_1710498210123_123456_m_000045_0 killed because it was running for too long说明Task超时需检查mapreduce.task.timeout或数据倾斜。HDFS访问模式用hdfs fsck /data/table_name -files -blocks查看文件数量、平均大小、Block分布。若发现大量1MB文件基本可判定小文件问题。某次调优中基线显示MR任务耗时14分32秒EXPLAIN显示2个Reduce StageYARN logs中Spill次数达12万次fsck显示12.7万个文件平均大小仅1.2MB。问题根源清晰小文件Shuffle溢出。4.2 第二步小文件治理2小时小文件是Hive性能杀手必须前置解决。方案分三级紧急止血用ALTER TABLE table_name CONCATENATE仅ORC/Parquet合并小文件。该命令在后台启动MapReduce任务将同一目录下小文件合并为标准大小如256MB。注意执行期间表不可写需安排在业务低峰期。长效防控在ETL写入端设置hive.merge.mapfilestrueMap端合并、hive.merge.mapredfilestrueReduce端合并并调大hive.merge.size.per.task256000000256MB。源头根治修改上游Kafka消费程序将batch.size16384默认提升至131072增加单次写入数据量同时启用Flink的StreamingFileSink的bucket_interval按时间窗口滚动写入避免高频小写。实测某日志表治理后文件数从12.7万降至840平均大小升至302MBMR任务耗时从14分32秒降至5分18秒。4.3 第三步引擎适配与参数初筛1小时根据基线诊断结果选择引擎若任务含大量ORDER BY、DISTRIBUTE BY、复杂DAG如多层嵌套子查询首选Tez若任务简单单表聚合、无Join、集群MR生态成熟MR更稳。然后用参数矩阵快速筛选| 参数类别 | MR推荐值 | Tez推荐值 | 调整依据 ||----------|-----------|------------|------------|| 内存上限 |mapreduce.map.memory.mb4096|hive.tez.container.size3072| 匹配NodeManager内存的70% || 排序缓冲 |mapreduce.task.io.sort.mb1024|tez.runtime.io.sort.mb1536| 避免spill留20%余量 || 并行度 |hive.exec.reducers.bytes.per.reducer512000000|tez.grouping.min-size134217728| 按基线文件大小分布计算 |所有参数在hive-site.xml中统一配置避免SQL中SET污染。4.4 第四步执行计划深度优化2小时参数只是杠杆执行计划才是支点。重点优化三类场景分区裁剪失效检查WHERE条件是否用变量如${dt}确保变量在编译期可解析对LIKE模糊查询避免前导通配符%abc改用REGEXP或倒排索引。Join顺序错误Hive默认按FROM顺序Join但最优顺序应是小表优先。用/* MAPJOIN(small_table) */强制指定或调整SQL中表顺序。谓词下推失败EXPLAIN中若Filter Operator出现在FileScan之后说明过滤未下推。检查UDF是否标有Description注解或改用内置函数。某报表任务原SQLSELECT a.id,b.name FROM fact_order a JOIN dim_user b ON a.uidb.id WHERE a.dt2024-03-15EXPLAIN显示Filter在Join后耗时8分。改为SELECT /* MAPJOIN(b) */ a.id,b.name FROM (SELECT * FROM fact_order WHERE dt2024-03-15) a JOIN dim_user b ON a.uidb.idFilter下推至Scan耗时降至1分22秒。4.5 第五步压力测试与参数微调3小时用tpcds-kit生成10GB测试数据运行核心SQL 5轮记录平均耗时、StdDev、YARN资源峰值。关键观察点若StdDev 15%说明存在不稳定因素如GC抖动、网络波动需检查-XX:UseG1GC等JVM参数若CPU利用率 40%可能是I/O瓶颈调大tez.runtime.io.sort.mb若内存利用率 90%需降低container.size或优化SQL减少中间数据。我们采用二分法微调以tez.runtime.io.sort.mb为例初始设1536MB测试后CPU利用率65%StdDev 8%则尝试1792MB若出现OOM则回退至1280MB。最终确定1664MB为最优值。4.6 第六步灰度发布与监控埋点30分钟上线不等于结束。在hive-site.xml中添加property namehive.log.explain.output/name valuetrue/value /property property namehive.query.log.level/name valueINFO/value /property这样每次查询会在/tmp/hive-logs/生成explain_query_id.txt便于事后审计。同时在Prometheus中配置HiveServer2指标hive_server2_queries_total{statussuccess}、hive_server2_query_duration_seconds设置告警若P95耗时突增200%立即通知。4.7 第七步效果验证与知识沉淀1小时上线后持续监控72小时对比基线指标基线优化后提升平均耗时14m32s2m18s85%YARN Container失败率8.7%0.2%98%HDFS读取量1.2TB0.3TB75%最后将本次调优的EXPLAIN截图、参数变更列表、测试数据、监控图表整理成内部Wiki标题为《Hive调优案例库电商日志分析任务V1.0》供团队复用。记住调优不是一次性动作而是将经验转化为可复用的资产。5. 常见问题与避坑指南那些文档里不会写的实战教训5.1 “调了参数为什么没效果”——四大隐形陷阱提示90%的“无效调优”源于未识别这些隐藏条件。陷阱一参数作用域冲突Hive参数有四个作用域HiveServer2全局Session会话SQL HintHive CLI配置。若你在hive-site.xml中设hive.execution.enginetez但在Beeline中执行SET hive.execution.enginemr;后续所有SQL都走MR。更隐蔽的是某些JDBC连接串自带?hive.execution.enginemr参数会覆盖服务端配置。排查方法在SQL开头加SET hive.execution.engine;确认当前生效值。陷阱二执行计划缓存未刷新Hive会缓存执行计划Plan Cache尤其在HS2启用了hive.server2.cache.resultstrue时。你改了tez.grouping.min-size但旧计划仍在缓存中执行。解决方案执行INVALIDATE METADATA table_name;Impala语法Hive不支持或更可靠的方法——重启HiveServer2服务或在SQL中加/* NOCACHE */提示。陷阱三底层存储格式不兼容曾有团队将hive.exec.orc.split.strategyHYBRID用于TextFile表结果任务直接失败。因为HYBRID策略依赖ORC/Parquet的元数据如Stripe信息TextFile无此结构。务必确认DESCRIBE FORMATTED table_name中InputFormat为org.apache.hadoop.hive.ql.io.orc.OrcInputFormat或org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat。陷阱四YARN资源未同步扩容调大hive.tez.container.size4096但YARN的yarn.scheduler.maximum-allocation-mb仍为3072导致容器申请被拒绝Task卡在ACCEPTED状态。必须同步调整YARN参数yarn.scheduler.maximum-allocation-mb ≥ container.size且yarn.nodemanager.resource.memory-mb需预留足够空间。5.2 “为什么Tez比MR还慢”——五个反直觉真相注意这些情况Tez天然劣势强行切换只会更糟。真相一超小数据集100MBTez的DAG编译、顶点调度、AM通信开销约2~5秒而MR的JobClient启动仅需1秒。处理10MB测试数据时Tez耗时8.2秒其中调度占6.1秒MR仅3.4秒。结论小任务用MR大任务用Tez。真相二强顺序依赖的SQL如INSERT OVERWRITE TABLE t1 SELECT ...; INSERT OVERWRITE TABLE t2 SELECT * FROM t1;Tez虽能优化为DAG但t2必须等t1完全写入才能开始读。此时MR的简单性反而更高效因为无需维护顶点状态机。真相三高延迟网络环境Tez顶点间通过Netty传输数据若集群跨机房如北京IDC访问上海HDFS网络RTT50msTez的管道传输优势被抵消甚至因重试机制导致延迟更高。此时MR的磁盘落盘反而更稳定。真相四UDF大量使用反射Tez的TaskClassLoader对反射调用更敏感。某自定义JSON解析UDF在MR下耗时1.2秒/行在Tez下飙升至4.7秒/行。原因是Tez的类加载器层级更深Class.forName()开销增大。解决方案改用ObjectInspector接口或预加载Class。真相五Hive版本过低Hive 2.3.0以下版本Tez优化器不成熟EXPLAIN常显示冗余顶点。升级到Hive 3.1.3tez.am.resource.memory.mb等参数才真正生效。某团队卡在Hive 2.1.0折腾两周无果升级后参数立竿见影。5.3 “如何快速判断该调哪个参数”——一张决策树就够了面对海量参数新手常不知所措。我总结了一张实战决策树覆盖95%场景开始 │ ├─ SQL是否含ORDER BY / SORT BY → 是 → 检查hive.limit.optimize.enable tez.runtime.sorter.class │ ├─ 是否有Join操作 → 是 → 查EXPLAIN是否Map Join → 否 → 调hive.auto.convert.join hive.mapjoin.smalltable.filesize │ │ │ └─ 是 → 检查小表大小 → 25MB → 确认hive.mapjoin.smalltable.filesize阈值 │ ├─ 是否有GROUP BY → 是 → 查EXPLAIN是否有Skew → 是 → 开hive.groupby.skewindata hive.groupby.mapaggr.checkinterval │ │ │ └─ 否 → 调hive.map.aggr hive.map.aggr.hash.percentmemory │ ├─ 任务是否频繁OOM → 是 → 查YARN日志 → Container Killed → 调container.size io.sort.mb │ │ │ └─ GC time高 → 调JVM参数-XX:UseG1GC -XX:MaxGCPauseMillis200 │ └─ 任务是否长尾95%快5%极慢 → 是 → 查EXPLAIN各Reducer数据量 → 差异10倍 → 启用Skew参数或加盐这张图已在某云厂商客户培训中验证工程师按图索骥平均30分钟内定位到核心参数效率提升4倍。5.4 我踩过的最深的三个坑血泪换来的经验坑一hive.optimize.index.filtertrue引发的数据错乱某次上线后报表中用户数突增200%。排查发现该参数启用后Hive会尝试用索引跳过某些Block但我们的ORC表未建索引Hive误判为“全匹配”跳过了部分数据块。关闭此参数后恢复正常。教训非必要不开启实验性参数尤其涉及数据正确性的。坑二tez.runtime.unordered.output.buffer.size.mb设为0的灾难为减少内存占用将该参数设为0期望禁用缓冲区。结果Tez强制启用最小值1MB但因配置冲突所有Task的输出缓冲区被初始化为1字节导致数据写入失败。日志中满屏java.nio.BufferOverflowException。正确做法不设为0而是设为合理值如64MB。坑三hive.exec.dynamic.partition.modenonstrict的权限漏洞开启此模式后动态分区可插入任意路径某开发误写INSERT OVERWRITE TABLE t PARTITION(dt) SELECT ..., 2099-01-01 as dt将测试数据写入生产分区覆盖了真实数据。此后我们强制要求动态分区必须配合hive.exec.max.dynamic.partitions.pernode默认100和hive.exec.max.dynamic.partitions默认1000双重限制。6. 进阶思考超越参数的系统性优化视角调优走到深处会发现参数只是冰山一角。真正的瓶颈往往藏在更底层。某次为某视频平台优化推荐日志分析我们已将Tez参数调至极致耗时仍卡在6分12秒。EXPLAIN显示所有顶点正常YARN资源充足但hadoop fs -du -h /tmp/hive-*发现临时目录堆积了2.3TB数据。根源在于Hive默认将UNION ALL的每个分支结果写入独立临时目录而该SQL有12个分支。解决方案不是调参数而是重构SQL逻辑用WITH子句提取公共表表达式将12分支合并为3个主分支临时目录数据降至0.4TB耗时降至2分07秒。这揭示了一个本质Hive调优的终极形态是用SQL工程思维替代参数工程思维。当你能熟练运用LATERAL VIEW explode()替代多层嵌套JOIN用WINDOW FUNCTION替代自关联用INSERT ... VALUES批量写入替代逐条INSERT参数调优的空间自然就缩小了。另一个维度是基础设施协同。我们推动运维团队将HDFS的dfs.datanode.max.transfer.threads从4096提升至8192dfs.client.read.shortcircuit.streams.cache.size从256调至1024配合Hive的hive.exec.orc.zerocopytrue使ORC零拷贝读取生效单节点吞吐提升37%。这说明最好的Hive调优从来不是孤立地改hive-site.xml而是让计算引擎、存储系统、网络架构形成合力。最后分享一个小技巧在hive-site.xml中添加propertynamehive.server2.logging.operation.level/namevalueEXECUTION/value/propertyHiveServer2会将每条SQL的执行阶段Compile、Execute、Fetch耗时写入日志帮你精准定位慢在哪一环——是编译慢SQL太复杂、执行慢数据倾斜、还是取数慢网络或客户端瓶颈。这个细节让我们的平均问题定位时间从2小时缩短至20分钟。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表