人脸识别项目如何在Spring Boot项目中如何建立数据库和管理
讲讲在Spring Boot项目中如何建立数据库和管理
随着智能门禁、考勤、访客系统的普及,人脸识别模块已成为众多项目的重要组成部分。在高性能的人脸识别系统中,数据库主要负责持久化存储,而识别比对操作应全部在内存中完成,以保证毫秒级响应和高并发性能。本文将介绍如何在 Spring Boot 项目中建立一个高效、可扩展的人脸识别数据库,并实现基础的人脸数据管理功能,同时结合 MySQL、Redis 与内存优化策略,提升系统整体性能。
一、基础入门介绍
这里为了能让大家对人脸识别SDK结合数据库有个初步了解,我们先利用 ArcSoftFaceDemo ,讲解下此Demo是如何对人脸数据库进行管理的,以及如何正确地注册到 ArcFace SDK 的引擎中,以支持后续的人脸检测和识别功能。下面结合代码分析整个管理流程。
1、数据库环境准备
在开始使用 ArcsoftFaceDemo 前,需要先准备好 MySQL 数据库环境。为了快速搭建,可以直接使用 Docker 启动一个数据库实例。
使用 Docker 启动 MySQL 8.0
执行以下命令启动 MySQL 容器:
docker run -d \
--name arcsoft_mysql \
-e MYSQL_ROOT_PASSWORD=Root123 \
-e MYSQL_DATABASE=arcsoft_face \
-p 3306:3306 \
registry.cn-hangzhou.aliyuncs.com/china-images/mysql:8.0
说明:
--name arcsoft_mysql:容器名称,方便管理。MYSQL_ROOT_PASSWORD=Root123:root 用户密码。MYSQL_DATABASE=arcsoft_face:初始化创建数据库arcsoft_face。-p 3306:3306:将容器端口映射到宿主机,方便程序连接。registry.cn-hangzhou.aliyuncs.com/china-images/mysql:8.0:指定使用 MySQL 8.0 阿里云镜像,如果有外网环境,可直接使用mysql:8.0代替。
如果你想了解更多 Docker 使用方式,可以参考其他 Docker 入门文档或教程。也可直接在物理操作系统上安装MySQL
数据库连接信息
启动完成后,你就可以通过以下信息连接数据库:
- URL:
物理机IP:3306 - 用户名:
root - 密码:
Root123 - 数据库:
arcsoft_face
这样就完成了数据库环境的搭建,为 ArcsoftFaceDemo 的人脸库管理做好了基础准备。
2、工程导入与数据库配置
在完成数据库环境准备后,下一步是导入 ArcSoftFaceDemo 工程,并将默认数据库配置改为 MySQL。
导入工程
- 从 ArcSoft 官网下载 ArcSoftFaceDemo 工程压缩包。
- 解压后,用 IntelliJ IDEA 打开工程目录。
修改数据库配置
工程默认使用的是 H2 内存数据库,适合快速演示和测试。但在正式环境中,建议切换到 MySQL 或其他持久化数据库。
打开 application.properties 文件,找到默认配置:
spring.datasource.url=jdbc:h2:file:./data/arcdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:db/schema-h2.sql
说明:
spring.datasource.url:H2 数据库文件路径。spring.datasource.driver-class-name:H2 数据库驱动。spring.datasource.username/spring.datasource.password:数据库账号密码。spring.h2.console.*:H2 Web 控制台相关配置,方便调试。spring.sql.init.*:初始化 SQL 文件路径,用于创建示例表结构。
H2切换为MySQL
在正式环境中,我们需要将默认 H2 配置改为 MySQL。打开 application.properties,将数据库相关配置修改为 MySQL 连接信息:
spring.datasource.url=jdbc:mysql://localhost:3306/arcsoft_face?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=Root123
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:db/schema-mysql.sql
说明:
spring.datasource.url:MySQL 连接 URL,包含数据库名arcsoft_face,字符集与时区设置。localhost地址根据实际数据库IP地址设置spring.datasource.driver-class-name:MySQL 驱动类。spring.datasource.username/spring.datasource.password:MySQL 账号密码,对应 Docker 启动 MySQL 时设置的用户名和密码。spring.sql.init.*:初始化 SQL 文件路径,用于创建用户表结构(与 H2 不同,需要使用 MySQL 版本 SQL)。
验证数据库连接
- 启动 ArcSoftFaceDemo 工程。
- 检查启动日志,确认数据库连接成功:
HikariPool-1 - Starting...
HikariPool-1 - Start completed.
注意:使用 MySQL 客户端(如 mysql 命令行或 Navicat等)连接数据库,确认 arcsoft_face 数据库已创建成功。
创建用户表
如果 MySQL 数据库中没有表,需要执行初始化 SQL 创建 user_info 表:
CREATE TABLE IF NOT EXISTS user_info (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(60) NOT NULL,
extra_info VARCHAR(500),
face_feature BLOB,
image_url VARCHAR(120),
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY user_info_name_uindex (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
关键字段说明:
id:用户唯一标识。name:用户名。face_feature:ArcSoft SDK 提取的人脸特征二进制数据。这里也可以根据业务来,使用text类型,存储base64编码后的字符串数据image_url:注册证存储地址。
这样就完成了 MySQL 数据库接入 ArcSoftFaceDemo 的准备工作。
3、人脸注册过程说明(FaceService.java)
FaceService 是整个系统的人脸注册与管理核心逻辑,负责:
- 从数据库读取用户人脸信息
- 注册/更新人脸到 ArcFace SDK 内存引擎
- 删除人脸
- 系统启动时批量加载人脸
- 支持从数据库、本地图片、classpath 目录三种来源初始化
下面详细说明各功能模块的逻辑。
人脸注册核心方法:addUser
public void addUser(String name, byte[] faceFeature) {
UserInfo userInfo = userInfoMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getName, name));
if (userInfo == null) {
UserInfo userInfoInsert = new UserInfo();
userInfoInsert.setId((int) (System.currentTimeMillis() % 1_000_000_000)); //如果是MySQL数据库,可去掉这个,利用数据库自增长id来生成
userInfoInsert.setName(name);
userInfoInsert.setFaceFeature(faceFeature);
userInfoMapper.insert(userInfoInsert);
faceEngineService.registerFaceFeature2Engine(userInfoInsert.getId(), name, faceFeature);
} else {
UserInfo userInfoUpdate = new UserInfo();
userInfoUpdate.setId(userInfo.getId());
userInfoUpdate.setName(name);
userInfoUpdate.setFaceFeature(faceFeature);
userInfoMapper.updateById(userInfoUpdate);
faceEngineService.removeFaceFeature2Engine(userInfo.getId());
faceEngineService.registerFaceFeature2Engine(userInfo.getId(), name, faceFeature);
}
}
功能说明
addUser 是 ArcSoftFaceDemo 项目中最核心的人脸注册方法,主要功能是:
- 将输入的人脸特征(byte[])写入数据库
- 将人脸特征注册到 ArcFace 引擎的内存特征库(内存中的索引结构)
- 同步维护两端的数据一致性(数据库 ↔ 识别引擎)
ArcFace SDK 的识别能力依赖于内存中的特征库,因此每次注册、更新、删除都必须保持数据库与 SDK 内存库一致。在做新增的时候,同步两边需同时新增,保证数据一致性。
这里注册存储的时候,也可以同时带一些业务上的信息,如人员id,人员姓名,人员注册照存放地址等。这些信息,在数据库中可以以字段的形式进行存储归档,可参考上面user_info表进行字段扩展优化。如果想通过SDK的searchFaceFeature接口获取到这些额外信息,在registerFaceFeature接口中提供了faceTag的一个字符串类型,用于存放业务数据,可以把这些信息以JSON字符串形式存放到faceTag中去,当然也可通过searchId,再查询数据库的方式,把对应的人员信息查询出来
ArcFace SDK 提取的人脸特征是一个 byte[] 数组。5.0 的中模型特征长度在 2056字节,大模型特征特征长度3080字节,这里在做数据库字段大小的时候需要关注下
两种存储方式:SDK的特征类型是byte[]数组类型,存储到数据库方式:
1、可以直接使用BLOB类型,一般推荐用BLOB类型存储,可以减少编解码带来的性能损失
2、byte[]数组转成base64字符串,数据库用TEXT类型进行存储,如果涉及到网络传输,可使用Base64字符串
删除用户 removeUser
public void removeUser(String name) {
UserInfo userInfo = userInfoMapper.selectOne(Wrappers.<UserInfo>lambdaQuery().eq(UserInfo::getName, name));
if (userInfo != null) {
faceEngineService.removeFaceFeature2Engine(userInfo.getId());
userInfoMapper.deleteById(userInfo.getId());
}
}
- 根据用户名查询用户
- 从 SDK 内存引擎移除人脸
- 从数据库删除记录
系统启动时自动加载人脸
@PostConstruct
public void loadImage2Engine() {
if ("true".equals(faceImageClear)) {
removeAllUser();
initSystemFace();
File file = new File(testImagePath);
if (file.isDirectory()) {
File[] files = file.listFiles();
log.info("开始加载本地人脸图片到引擎中");
for (File imageFile : files) {
String name = imageFile.getName();
if (name.endsWith(".jpg") || name.endsWith(".png")) {
String userName = name.substring(0, name.lastIndexOf("."));
register(userName, imageFile, null);
}
}
log.info("加载本地人脸图片到引擎中完成");
}
}else {
initDatabaseFace2Engine();
}
}
系统启动后执行,用于初始化内存人脸库,为了加快识别速度,建议每次启动服务后,就直接把数据库里的人脸注册信息放到SDK引擎里,减少下次识别读取数据库带来的耗时增加。Demo中提供了有两种模式,根据配置文件的register.faceimage.clean进行切换
register.faceimage.clean=true,每次启动会清空数据库,重新加载本地图片进行注册,同时存储到数据库和SDK
register.faceimage.clean=false,每次启动从数据库读取特征,加载到SDK。Demo为了测试,会同时把本地本次未注册的图片进行存储和注册,生产环境可只保留从数据库读取加载
4、人脸识别方式
这里再另外提一下,SDK人脸识别主要有以下两种方案,Demo采用的是第2种方案,也可根据实际业务使用第1种方案
①、使用 Map 列表 + compareFaceFeature 接口
- 原理:将注册的人脸特征存放在 Map 或列表中,通过 SDK 的
compareFaceFeature接口进行循环比对。 - 优点:可灵活分组管理人脸库;支持多线程并发比对。
- 缺点:当人脸库量大时,频繁调用比对接口会增加性能损耗。
②、使用 SDK 提供的原生接口注册
- 原理:利用
registerFaceFeature、removeFaceFeature、updateFaceFeature、searchFaceFeature组合接口,实现对人脸的增删改查 - 优点:SDK 原生实现比对,性能快,资源消耗少。
- 缺点:不支持分组比对;系统重启后需重新注册特征;多引擎并发时,每个引擎的人脸库需单独管理,维护复杂。
为了实现长久保存和分布式共享,人脸特征需进行合理存储
5、人脸引擎说明(FaceEngineService.java)
FaceEngineService 是人脸SDK 的核心部分,负责:
- 人脸检测、特征提取、活体检测、属性检测等
- 人员信息的新增、删除、修改、人脸搜索
下面详细说明各功能模块的逻辑。
人员新增:registerFaceFeature2Engine
public void registerFaceFeature2Engine(int searchId, String faceTag, byte[] faceFeature) {
FaceEngineFactory factory = (FaceEngineFactory) faceEngineComparePool.getFactory();
List<FaceEngine> allObjects = factory.getAllObjects();
FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo();
faceFeatureInfo.setSearchId(searchId);
faceFeatureInfo.setFaceTag(faceTag);
faceFeatureInfo.setFeatureData(faceFeature);
// log.info("Register:"+ JSON.toJSONString(faceFeatureInfo));
for (FaceEngine allObject : allObjects) {
int res = allObject.registerFaceFeature(faceFeatureInfo);
}、、
}
这里重点说明下,因为我们的FaceEngine用了线程池,同时初始化了多个引擎,所以我们在向SDK注册人员信息的时候,需要给每个引擎进行注册,删除同理。
人员新增:removeFaceFeature2Engine
public void removeFaceFeature2Engine(int searchId) {
FaceEngineFactory factory = (FaceEngineFactory) faceEngineComparePool.getFactory();
List<FaceEngine> allObjects = factory.getAllObjects();
for (FaceEngine allObject : allObjects) {
allObject.removeFaceFeature(searchId);
}
}
删除同注册接口,需要同步向所有FaceEngine库进行移除操作
二、人脸特征三层存储与分布式管理
上面已经介绍了基本的数据库接入和使用,那么在实际人脸识别场景下,系统的数据存储与识别通常采用三层设计:
- MySQL:负责持久化存储人脸特征信息,实现可靠的长期保存。
- Redis:用于异步更新与分布式缓存,实现多机器环境下的人脸特征共享。
- 内存(ConcurrentHashMap / 特征池):存储当前在线的活跃人脸特征,实现毫秒级识别。
这种设计保证了系统既能持久化存储,又能在识别时获得最佳性能。
在 Spring Boot 项目中,实现人脸特征的管理可以从数据库建表、服务层加载、Redis缓存、特征注册与比对几个方面进行详细设计。
在多台识别服务器部署的分布式环境中,人脸特征需要在各节点之间保持一致性、高可用和高性能。结合内存、Redis 和数据库三层存储,设计参考方案:
1、 启动加载策略
- 流程:
- 每台识别服务器启动时,先从 Redis 拉取最新的人脸特征到本地内存缓存。
- 如果 Redis 中没有特征数据(如初次部署),则从 MySQL 数据库加载。Redis 可作为 内存缓存 + 异步更新机制,保证各节点识别数据一致性
- 加载后,内存缓存即可支持毫秒级人脸识别。
- 实现示例:
@PostConstruct
public void loadFeaturesOnStartup() {
Set<String> keys = redisTemplate.keys("face:*");
if (keys != null && !keys.isEmpty()) {
for (String key : keys) {
byte[] featureData =(byte[]) redisTemplate.opsForValue().get(key);
if (featureData != null) {
featureMap.put(key.replace("face:", ""), featureData);
}
}
} else {
// Redis为空,从数据库加载
List<FaceFeature> features = repository.findAll();
for (FaceFeature f : features) {
featureMap.put(f.getUserId(), f.getFeature());
redisTemplate.opsForValue().set("face:" + f.getUserId(), f.getFeature());
}
}
}
- 优势:
- 避免每次识别都访问数据库,提高性能。
- 启动即加载最新特征,保证识别数据实时性。
2、 特征更新与同步策略
在分布式场景下,用户新增、删除或更新人脸特征时,需要保证所有节点内存保持一致:
-
更新顺序:
写内存:立即更新本地内存缓存,保证识别服务实时可用。
- 写 Redis:同步到 Redis,作为分布式共享层。
- 写数据库:异步持久化到 MySQL,保证长期数据安全。
-
同步机制:
- 利用 Redis Pub/Sub 或消息队列(Kafka、RabbitMQ)广播更新事件。
- 其他节点接收到更新事件后,刷新本地内存特征缓存。
- 示例逻辑:
public void updateFeature(String userId, byte[] feature) {
// 更新本地内存
featureMap.put(userId, feature);
// 更新 Redis
redisTemplate.opsForValue().set("face:" + userId, feature);
// 发布更新事件通知其他节点
redisTemplate.convertAndSend("face:update", userId);
// 异步更新数据库
asyncUpdateDatabase(userId, feature);
}
// 订阅事件刷新内存
@EventListener
public void handleRedisUpdateEvent(Message message) {
String userId = (String) message.getBody();
String feature = redisTemplate.opsForValue().get("face:" + userId);
if (feature != null) {
featureMap.put(userId, feature);
}
}
通过合理的存储架构和内存优化,Spring Boot 项目的人脸识别系统可以在保证高性能的同时,实现可扩展、分布式的人脸管理功能,为智能门禁、考勤和访客系统提供稳定支撑。

浙公网安备 33010602011771号