news 2026/9/8 13:33:28

EMR中Hive与Spark集成Glue Data Catalog实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
EMR中Hive与Spark集成Glue Data Catalog实战指南

1. 问题背景:为什么要在EMR里折腾Glue Data Catalog

先把话说在前面:如果你在EMR上只用Spark、不跑Hive,那Glue Data Catalog可能只是一个“锦上添花”的东西。但只要你同时用Hive和Spark,尤其在一个团队、一套数仓流程里既有hive命令行操作,又有spark-submit跑批任务,那这两者能不能“看到同一张表”就是决定性的问题了。我最初遇到的场景就是这么接地气:上午用Hive建了一张分区表,下午Spark作业直接报Table not found,查了一下才发现两者各自维护了一套元数据,压根不互通。这种割裂感在做数据仓库的时候特别致命。

EMR集群默认自带的元数据服务是Hive Metastore,但这个Metastore默认落在集群本地。EMR和自建Hadoop最大的区别在于:它不是一台常驻机器,而是按需拉起、用完销毁的。集群一终止,本地Metastore里存的库表信息就没了。即使集群不销毁,只要你有多个集群(比如开发集群、生产集群分开),每个集群各有一份Metastore,那Hive里建的表Spark看不到,Spark里建的表Hive也看不到,更麻烦的是你可能根本说不清哪一份数据是最新的。

Glue Data Catalog这时候就变成一个很自然的选择。它是AWS上的托管元数据服务,本质是一个“集中式的Hive Metastore”,底层兼容Hive Metastore的协议。EMR在创建集群的时候可以直接指定用它来替代本地的Metastore,这样Hive和Spark只要都指向同一个Glue Data Catalog,两边的库表元数据就统一了。听起来像个很常规的配置,但实际集成过程中牵扯到的细节远不止“开个开关”这么简单。

这篇文章就从我自己的操作经验出发,把Hive和Spark集成Glue Data Catalog的完整链路拆开讲一遍,包括架构理解、具体配置、参数选择,还有我踩过的坑和排查思路。如果你也在用EMR搭数仓,或者正被“Hive和Spark元数据不互通”折磨,这篇应该能帮你省不少时间。

2. 整体架构拆解:Glue Data Catalog到底在整条链路里扮演什么角色

2.1 先搞清楚Hive Metastore的工作机制

在进入Glue Data Catalog之前,必须先把Hive Metastore的原理讲透。很多人在这一步就懵了,因为Hive的部署结构天然比Spark复杂一点。

Hive本质上分两层:执行引擎和元数据服务。执行引擎干活,比如跑MapReduce、跑Tez,但表结构、分区信息、字段注释这些“档案”都存放在Metastore里。Metastore本身又是一个独立的服务,后端可以接Derby、MySQL、PostgreSQL这类关系型数据库。HiveServer2在启动时会去连Metastore,客户端用beeline连接HiveServer2之后,你执行的CREATE TABLE语句实际是HiveServer2转给Metastore去写库的。

Spark集成Hive的时候逻辑稍微有点不一样。Spark并不强制要求启动HiveServer2,它只需要能读到Metastore的元数据就行。所以spark.sql.catalogImplementation=hive这个配置的意思就是告诉Spark:“你不要自己管元数据,去连一个Hive Metastore”。Spark里内置了对Metastore客户端的支持,启动时会直接用Hive Metastore Client去连远端的Metastore服务。

在自建Hadoop集群里,Hive和Spark如果共用同一个MySQL作为Metastore后端,那两边天然就能看到同一批表。但在EMR里这个逻辑被“包装”了一层:EMR自带的Metastore后端就是用集群本地的一个嵌入式数据库,默认并不适合多个组件共享,更不适合集群销毁后继续保留。所以你需要的核心能力是:让两个计算框架都指向一个“外部、持久、共享”的元数据存储,Glue Data Catalog就是干这个的。

2.2 Glue Data Catalog没你想的那么“重”

很多人一听Glue,第一反应是“ETL服务”,因为AWS Glue确实有一个托管ETL的功能。但Glue Data Catalog其实是独立于ETL能力的一个组件,你可以完全不用Glue的ETL,只用它的Catalog功能。

Glue Data Catalog对外提供的是Hive Metastore协议的兼容接口。这意味着Hive和Spark不需要什么特殊改造,只需要把Metastore地址指到Glue的端点,就能把它当成一个远端的Hive Metastore来用。对上层计算框架而言,Glue Data Catalog看起来就是一个“永远在线、从来不丢数据”的Hive Metastore服务。

这种设计有几个好处。第一是持久性:集群销毁重建之后,所有表结构还在,HDFS或S3上的数据文件还在,重新拉起集群后一条SHOW TABLES就能直接恢复现场。第二是跨集群共享:开发集群、生产集群、临时调查集群,只要都指向同一个Glue Data Catalog,就能共用同一套元数据。第三是权限统一:可以用IAM策略精细控制谁能建表、谁能查表,而不是每套集群单独维护一份权限。

但“托管”也意味着你失去了一些控制权。Metastore的底库是什么样的你看不到,Metastore的GC、连接数你也不用管,但同样地,如果Glue服务性能出现抖动,你的Hive查询和Spark任务都会受影响。这一点在分区特别多的表上特别明显,后面我会单独讲。

2.3 数据文件与元数据分离的思维转换

理解Glue Data Catalog的另一个关键点,是彻底抛弃“Metastore里存了数据”这个错觉。Metastore存的全是元数据,真正的数据还是在你的存储系统里,EMR上最常见的就是S3。

建表的时候,Hive或Spark会把表的LOCATION指向一个S3路径,Metastore只负责记录“这张表的字段有哪些、分区有哪些、数据在哪个路径”。查询的时候,计算引擎先访问Metastore拿到元数据,再根据LOCATION去S3拉数据,最后在内存里做计算。

所以在做集成方案时,千万不要把“元数据统一”和“数据统一”混为一谈。Glue Data Catalog解决的是前者,让Hive和Spark拿到同一份“数据地图”。至于S3上的数据文件格式是不是两头都能读,那是另一回事,也就是我们常说的存储格式兼容问题。比如Spark默认写的Parquet,Hive能读;但Hive写的某些TextFile格式,Spark也能读。这跟Metastore用哪个没直接关系,是SerDe和文件格式层面的问题。我在后面的实操里会说怎么验证这种兼容性。

3. Hive与Spark集成Glue Data Catalog的配置实操

3.1 EMR集群创建时指定Glue作为Metastore

最简单的接入方式是在创建EMR集群时直接指定。控制台和CLI都支持,核心就一个参数:--configurations里设置hive-sitehive.metastore.glue.catalogid,或者在EMR的分类配置里直接选Glue Data Catalog作为Hive Metastore。

我一般习惯用CLI创建,配置写成一个JSON文件维护,方便版本管理。下面是一个典型的配置文件,里面已经包含了Hive和Spark两个组件需要指向Glue的配置:

[ { "Classification": "hive-site", "Properties": { "hive.metastore.glue.catalogid": "123456789012", "aws.glue.endpoint": "glue.us-east-1.amazonaws.com", "hive.metastore.client.capability.check": "false" } }, { "Classification": "spark-hive-site", "Properties": { "hive.metastore.glue.catalogid": "123456789012", "aws.glue.endpoint": "glue.us-east-1.amazonaws.com" } } ]

这里hive.metastore.glue.catalogid就是你的AWS账号ID,一般默认就是当前账号的Glue Catalog,不填也会自动使用当前账号的。但如果你用的是Lake Formation做细粒度权限管理,或者跨账号共享Catalog,那这个参数就必须显式指定了。aws.glue.endpoint理论上也可以不配,它会根据Region自动解析,但如果你的VPC里配置了Glue VPC Endpoint,建议显式指一下内网地址,免得流量绕公网。

还需要注意hive.metastore.client.capability.check。Hive 3.x版本启动时会做Metastore能力校验,连Glue这种托管服务时,某些Capability(比如ALREADY_FILTERED)可能没法正确响应,导致Hive启动报错。我遇到的情况是Hive CLI启动慢、偶尔报能力检查失败的错,把这个开关设成false之后就稳了。如果你的EMR版本比较新,这条配置可能已经不必要,但保留它不会有副作用。

Spark侧为什么是spark-hive-site而不是hive-site?因为EMR上Spark读取Hive配置时有自己的优先级,spark-hive-site.xml里配置的值会覆盖Spark进程里Hive Metastore相关的默认配置,比直接改hive-site.xml更精准。如果你在EMR控制台里操作,界面上也会让你在Spark的hive-site分类里填Glue配置,道理是一样的。

启动命令示例:

aws emr create-cluster \ --name "emr-glue-catalog-demo" \ --release-label emr-6.12.0 \ --applications Name=Hive Name=Spark Name=Tez \ --ec2-attributes InstanceType=m5.xlarge,InstanceCount=3 \ --instance-groups InstanceGroupType=MASTER,InstanceType=m5.xlarge,InstanceCount=1 \ InstanceGroupType=CORE,InstanceType=m5.xlarge,InstanceCount=2 \ --configurations file://./glue-config.json \ --use-default-roles

集群起来之后,先别急着建表,先验证一下Hive和Spark是不是真的都指向Glue了。

3.2 验证Hive侧是否成功连接Glue Data Catalog

集群起来之后,先在Master节点上跑一下Hive的命令。注意,我建议用beeline连HiveServer2做验证,而不是直接在Master上执行hive命令行,因为后者触发的是一条完整MapReduce或Tez任务,跑得慢,而且输出结果里不一定能看到Metastore信息。用beeline更直接。

beeline -u "jdbc:hive2://localhost:10000/default" -n hadoop

连上之后执行:

SHOW DATABASES; CREATE DATABASE test_glue_db; CREATE TABLE test_glue_db.employee ( id INT, name STRING, dept STRING ) PARTITIONED BY (dt STRING) STORED AS PARQUET LOCATION 's3://my-bucket/warehouse/employee';

执行完之后,打开AWS控制台进入Glue服务,在Data Catalog的Databases列表里应该能看到test_glue_db,Tables列表里能看到employee。只要出现了,就说明Hive侧已经写到Glue里了。

我特别强调这个验证步骤,是因为很多人配置完直接跑业务,等出了问题才回头查底层。实际上5分钟就能确认的事情,没必要拖到数仓里几百张表之后才发现建表路径不对。

注意一下建表时的LOCATION。在EMR+S3的组合里,表的真实数据写在S3,元数据写在Glue。如果你不指定路径,Hive会用默认的/user/hive/warehouse目录,这在本地集群可能没问题,但在EMR上如果不是放S3,集群销毁数据也就没了。所以尽量在建表时显式指定S3路径。

3.3 Spark侧读取Glue中的元数据

Hive写完了,接下来就看Spark能不能读到。

进入spark-sql交互环境:

spark-sql

然后执行:

SHOW DATABASES; USE test_glue_db; SHOW TABLES; SELECT * FROM employee;

如果Spark能列出employee表,说明Spark已经通过Glue Data Catalog读取到了Hive建的元数据。这时候整个集成链路已经通了,Hive和Spark开始共享同一套元数据。

但“读到”只是一个起点,接下来要验证“互写”。在Spark侧创建一个新表,然后回Hive侧看能不能看到:

CREATE TABLE test_glue_db.spark_created_table ( id BIGINT, value STRING ) USING PARQUET LOCATION 's3://my-bucket/warehouse/spark_created_table';

创建完成后,回到beeline执行:

SHOW TABLES IN test_glue_db;

如果能看到spark_created_table,恭喜你,双向互通已经验证完成。这套流程跑通之后,你的数仓架构里最烦人的元数据隔阂问题就消失了。

3.4 补充:使用Hive Metastore模式的Spark配置

有一种情况需要特别说明。如果你在EMR上不想用spark-sql,而是用spark-submit提交一个Scala或Python作业,那代码里大概率会用到SparkSession。SparkSession读取Glue Data Catalog的配置跟spark-sql命令行是一致的,但有些老代码里会显式设置enableHiveSupport(),这会触发Spark把Hive Metastore的配置加载进来。如果EMR上的spark-hive-site.xml已经正确配置,那没问题;但如果你的作业是在一个自建Spark环境里跑的,就需要在代码里或者提交命令里补上相关配置。

一个典型的spark-submit提交命令可以这样写:

spark-submit \ --master yarn \ --deploy-mode cluster \ --conf spark.sql.catalogImplementation=hive \ --conf spark.hadoop.hive.metastore.glue.catalogid=123456789012 \ --conf spark.hadoop.aws.glue.endpoint=glue.us-east-1.amazonaws.com \ application.py

注意spark.sql.catalogImplementation=hive很关键。Spark 3.x默认的Catalog实现是in-memory,这会导致spark.sql里看不到任何Hive表。你要明确告诉Spark走Hive Metastore协议,它才会去连Glue。

很多人在Spark作业里看不到Hive表,第一反应是“权限不够”或者“Glue配置错了”,但实际上大概率是catalogImplementation没改成hive。这个配置是Spark所有会话的“总开关”,优先级是最高的。所以我的建议是:提交作业时显式写出来,不要依赖环境的默认值。

4. 常见问题与排查技巧实录

4.1 Hive建的表Spark看不到,第一时间查什么

这个问题我在不同环境里遇到过不下五次,每次根因都不一样,但排查顺序基本是固定的。

第一步,确认Spark的Catalog实现。用spark-sql执行:

SET spark.sql.catalogImplementation;

如果没有返回hive,那不需要再看别的了,Spark压根没走Metastore。把它设为hive之后再试一次。

第二步,查看Spark进程里的Hive Metastore配置。在spark-sql里执行:

SET hive.metastore.uris; SET hive.metastore.glue.catalogid;

正常情况下,使用Glue Data Catalog时hive.metastore.uris应该是空或者没配置的,而hive.metastore.glue.catalogid应该有值。如果hive.metastore.uris指向了一个不存在的Metastore地址,那Spark就会去连那个地址,连不上就抛异常,连上了也不是Glue,自然看不到Glue里的表。

第三步,检查IAM权限。Spark作业的Execution Role必须有Glue相关权限,最基本的几个是glue:GetDatabaseglue:GetDatabasesglue:GetTableglue:GetTablesglue:GetPartitionglue:GetPartitions。如果权限缺失,Spark会报AccessDeniedException,这个错误比较明显。但有时候权限策略写得太复杂,比如用了Condition限定资源路径,那排查起来就麻烦一些。我的建议是先用一个宽松的Policy验证链路通畅,再加限制条件,一层层收紧。

4.2 建表都成功了,但两边查到的字段对不上

还有一种很隐蔽的情况:Hive和Spark都能看到表,但两边执行DESCRIBE的结果不一样,或者Hive能查出数据、Spark查出的结果全是NULL,反过来也一样。这种情况十有八九是存储格式兼容问题。

举一个我实际踩过的例子。Spark默认用USING PARQUET建表,底层文件是Parquet格式。Hive要求通过STORED AS PARQUET来识别Parquet,如果你的建表语句没指定STORED AS,Hive会按默认的TextFile格式去读,然后发现读出来的字段对不上。反过来也一样,Hive建表时STORED AS TEXTFILE,Spark虽然能读到元数据,但Spark读TEXTFILE时对字段类型和分隔符的处理方式跟Hive不完全一致,也可能导致读不出内容。

这个问题的本质在于:Metastore只是元数据地图,但真正读文件的时候,每个引擎还需要知道“文件的物理格式是什么、字段怎么解析”。Glue Data Catalog里存的SerDe信息、InputFormat、OutputFormat,如果两个引擎对同一份SerDe信息的解释有差异,就会出现“能看见但读不对”的情况。

我的习惯是:数仓里的核心表统一使用Parquet格式,并且两边的建表语句都显式指定格式。Hive侧用STORED AS PARQUET,Spark侧要么用USING PARQUET,要么为了跟Hive完全一致,直接通过CREATE TABLE ... STORED AS PARQUET这种方式建表。这样能最大程度避免格式识别差异。

还有一个相关但容易被忽略的点:字段注释和复杂类型。Hive对STRUCTMAPARRAY这类复杂类型的展示方式跟Spark有细微差别,但不是存储层面的错乱,只是显示的字符串格式不同。看到这种情况不用慌,用DESCRIBE对比一下字段类型和嵌套结构,只要基础类型一致,基本不影响查询。

4.3 使用SHOW CREATE TABLE导出并重建表

如果你已经有一批表在旧的Metastore里,想整体迁移到Glue Data Catalog,最快的办法不是手工重建,而是用SHOW CREATE TABLE把每张表的建表语句导出来,替换掉LOCATION里旧的路径,然后在新的Metastore下执行。

具体的批量操作可以写成脚本。先通过Hive把库下表名捞出来,然后用SHOW CREATE TABLE逐张导出,存成SQL文件。要注意几点:第一,旧的LOCATION如果指向HDFS路径,迁移到S3之后需要改成对应的S3路径;第二,如果表有大量分区,建表之后最好逐分区执行MSCK REPAIR TABLE或者用ALTER TABLE ADD PARTITION批量修复分区元数据;第三,如果表有权限控制,比如基于Lake Formation的列级权限,迁移后需要重新授权。

按照我的经验,几十张表的迁移是很快的,瓶颈一般不在加表的速度,而在分区修复。因为Glue Data Catalog的API调用有速率限制,分区特别多的表(比如上万个分区)如果一次性MSCK REPAIR,很容易触发限流。

4.4 Glue Data Catalog性能问题:分区多的时候Hive查询变慢

这个是集成之后比较常见的“慢性病”。表结构没问题、查询能出结果,但就是慢。尤其SHOW PARTITIONSSELECT COUNT(*)这类需要扫描大量分区元数据的操作,在Glue Data Catalog上会明显比本地Metastore慢。

原因不复杂:Glue Data Catalog的元数据操作是网络请求,每查一次分区都要调一次API,跟本地连数据库比天然有网络开销。如果你一张表有几千个分区,Hive在执行查询计划时需要枚举所有分区,这个枚举过程在Glue上就是几千甚至几万次API调用。

优化思路有两个方向。第一是提升单次请求的效率,调整hive.metastore.glue.max-retryhive.metastore.glue.num-retries这类参数,增加重试次数;把hive.metastore.glue.thrift.connection-timeout调大一点,避免网络波动时直接超时。第二是从业务侧优化,减少不必要的全分区扫描。比如按天分区的时候,能走分区裁剪就走分区裁剪,少用SELECT *全表扫。

另外一个细节是:如果一张表的分区非常多,但历史分区已经不需要了,可以考虑归档,比如把一年前的数据移到一个单独的库或单独的表里,别让它继续污染主表的元数据。这样Hive和Spark的元数据检索速度都能回来。

4.5 集群重启后Glue里的表“消失”了

还有一种情况容易引起恐慌:集群重启之后,SHOW TABLES返回空,第一反应是Glue里的元数据丢了。但绝大多数情况下不是丢了,而是新集群没有指向同一个Catalog。

EMR集群默认会按照创建时的配置决定Metastore用哪个。如果你在控制台创建集群时没有选Glue,或者配置分类里没有包含正确的spark-hive-site,那新集群会自建一个本地Metastore,跟Glue完全是两套,自然看不到Glue里的表。

这个问题的排查顺序是:登录新集群,执行SET hive.metastore.urisSET hive.metastore.glue.catalogid,确认是否指向了正确的Glue库。如果配置没问题,再在Glue控制台里确认Data Catalog下确实有对应表。两边都核对了还是看不到,才需要考虑是不是账号权限隔离或者Catalog被切换了。

还有一点要注意:Glue Data Catalog是有Region概念的。你在us-east-1建的库表,在us-west-2的EMR集群里默认是看不到的。EMR集群的Region和Glue的Region必须一致,或者至少EMR侧要显式指定跟Glue同一个Region的Endpoint。这个在跨Region部署的时候经常被踩。

5. 表格式与数据湖方案的选型建议

5.1 Hive和Spark的表存储格式怎么选更稳

如果你在EMR上同时用Hive和Spark,表的存储格式建议遵循一个基本原则:核心表用Parquet,不要用TextFile,也不要轻易用ORC。

Parquet在Spark里的支持是最成熟的,读写性能好,压缩率高,还支持列裁剪和谓词下推。Hive读Parquet也没问题,只要SerDe是org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe就行。

ORC的问题在于,虽然Spark 3.x已经支持读取ORC,但写入和读取时的优化参数跟Hive原生场景还是有差异。如果你是一个团队同时用两种引擎跑数仓任务,Parquet是最小公倍数,两边都不会有性能上的坑。

TextFile能不用就不用。不是不能读,而是TextFile在Spark上跑起来慢,而且字段类型有可能会被默认推断成string,跟Hive里定义的int、double不一致,到时候查出来类型不匹配又要返工。

5.2 分区策略:从Metastore角度看合理分区数量

分区是Hive和Spark性能优化的核心,但从Glue Data Catalog的角度看,分区不是越多越好。

我见过有人把表按小时分区,一天24个分区,跑了一年就是8760个分区,一张中等规模的表手动查一个SHOW PARTITIONS要卡好几秒。在这种场景下,即使查询能走分区裁剪,元数据枚举的延迟也会拖慢整体任务。

从实践来看,日分区是数仓最合理的默认选择。你需要的报表和跑批任务绝大多数都是“按天产出、按天消费”,小时级分区尽量只在实时性要求极高的场景用。另外,如果你的表是按天分区但一天有多批次数据,可以通过INSERT OVERWRITE覆盖当天分区,而不是把每小时都拆成一个分区。

分区数量过大还有一个隐患:Glue Data Catalog的API有配额,默认情况下单个账号每秒钟的API调用次数有上限。如果一张表的分区特别多,你的Spark任务在做全表扫描时触发的分区枚举API调用会非常密集,很容易触发限流,表现就是任务间歇性失败,报ThrottlingException

如果已经有一张大分区表,可以考虑用ALTER TABLE ... ARCHIVE PARTITION或者干脆做历史分区的冷热分离。冷数据挪到单独的表或单独的库,不仅Metastore轻了,查询速度也会快很多。

5.3 什么时候要升级到Lake Formation

Glue Data Catalog只是一个元数据目录,它本身不提供库表级别的细粒度权限。EMR上的Hive和Spark默认是通过IAM的glue:GetTable这类权限来控制访问,粒度到表。如果你需要列级、行级的安全控制,那就需要上Lake Formation。

Lake Formation跟Glue Data Catalog是深度集成的,你可以理解成给Glue Data Catalog套了一层统一鉴权层。Hive和Spark通过Lake Formation访问Glue里的元数据时,会把用户的IAM身份传过去,Lake Formation再做细粒度鉴权,然后返回允许访问的数据范围。

升级到Lake Formation不是必须的。如果你的团队规模不大、权限控制只到库或表级别,IAM策略完全够用。但如果你做的是金融、医疗这类对数据安全要求极高的项目,需要审计和细粒度控制,那尽早规划Lake Formation相关的改造会省更多后期的麻烦。

但有一个现实问题:Lake Formation的集成配置比单纯用Glue Data Catalog复杂,需要在EMR的安全配置里打开相关选项,还要为计算引擎配置专门的集成角色。上线前一定要在测试环境多验证几轮,别直接上生产。

6. 实操总结与效率提升经验

集成Glue Data Catalog这件事,本质上不是“配一下就能跑”的问题,而是一套元数据中心化的架构改造。完成Hive和Spark两个引擎的配置后,你的数仓体系等于有了一个稳定的数据地图层,后续开发、查询、权限控制都在这张地图上操作,效率和可维护性都会明显提升。

我在实际使用中感受最深的一点是:这套方案彻底省掉了“表结构同步”的日常操作。以前Hive建表、Spark还要同步信息,两边哪怕只差一个字段,跑批任务就可能挂掉。现在两边读的是同一份元数据,这个问题基本消失了,开发时间省出来不少。

最后分享两个小技巧。第一个,EMR集群如果经常销毁重建,建议把Glue相关的配置封装成一个独立的glue-config.json文件纳入代码库管理,不要每次手动在控制台里点。这样新建集群时一条CLI命令就能复现完全相同的Metastore配置,避免人为遗漏。第二个,在跑Spark作业前,先在spark-sql里执行一条SHOW TABLES确认能连上Glue,这个习惯能帮你早5分钟发现配置问题,而不是等作业跑了一半才报错。这些小细节看起来不起眼,但在生产环境里就是实实在在的稳定性和效率。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 13:28:23

Python unittest实战:从基础断言到Mock与CI集成

我最早接触unittest,其实是带着一点抵触情绪的。那时候觉得写测试又要多写一倍代码,还挤占了开发时间,项目排期摆在那里,能跑起来就不错了。直到有一次上线前改了一个工具函数,自认为改动很小,结果把另一个…

作者头像 李华
网站建设 2026/9/8 13:26:21

服务器异常与安全加固:从可疑进程排查到系统防护

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 13:25:51

视频流行人头部椭圆虚化:从检测到遮罩的隐私保护实战

开头先交代一个背景:我们团队在畅联云平台做视频能力建设,这一期专门处理“路人头部椭圆虚化”。说白了就是在实时视频流里检测每一个入镜的路人,把头部区域用一个椭圆形的模糊遮罩盖住,既保护路人隐私,又不影响监控主…

作者头像 李华
网站建设 2026/9/8 13:25:40

阿里云MaaS平台多人AI世界模型部署与优化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 13:25:21

DeepSeek Harness 架构解析:从工具调用到 Agent 开发实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 13:24:52

Qwen3-27B四卡横评:3090、4090、5090与V100实战对比

先交代一句:标题里的“Qwen3.8-27B”我猜实际指的是谁都能在社区里搜到的Qwen3-27B,可能只是随手多打了个点。下面正文里我用 Qwen3-27B 来写,参数和技术细节都按 Qwen3-27B 这个开放权重的 270 亿参数模型来算,这样对四张卡横评才…

作者头像 李华