人脸识别项目如何在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。

导入工程

  1. 从 ArcSoft 官网下载 ArcSoftFaceDemo 工程压缩包。
  2. 解压后,用 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)。

验证数据库连接

  1. 启动 ArcSoftFaceDemo 工程。
  2. 检查启动日志,确认数据库连接成功:
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());
        }
    }
  1. 根据用户名查询用户
  2. 从 SDK 内存引擎移除人脸
  3. 从数据库删除记录

系统启动时自动加载人脸

    @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 提供的原生接口注册

  • 原理:利用registerFaceFeatureremoveFaceFeatureupdateFaceFeaturesearchFaceFeature 组合接口,实现对人脸的增删改查
  • 优点: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库进行移除操作

二、人脸特征三层存储与分布式管理

上面已经介绍了基本的数据库接入和使用,那么在实际人脸识别场景下,系统的数据存储与识别通常采用三层设计:

  1. MySQL:负责持久化存储人脸特征信息,实现可靠的长期保存。
  2. Redis:用于异步更新与分布式缓存,实现多机器环境下的人脸特征共享。
  3. 内存(ConcurrentHashMap / 特征池):存储当前在线的活跃人脸特征,实现毫秒级识别。

这种设计保证了系统既能持久化存储,又能在识别时获得最佳性能。

在 Spring Boot 项目中,实现人脸特征的管理可以从数据库建表、服务层加载、Redis缓存、特征注册与比对几个方面进行详细设计。

在多台识别服务器部署的分布式环境中,人脸特征需要在各节点之间保持一致性、高可用和高性能。结合内存、Redis 和数据库三层存储,设计参考方案:

1、 启动加载策略

  • 流程
    1. 每台识别服务器启动时,先从 Redis 拉取最新的人脸特征到本地内存缓存。
    2. 如果 Redis 中没有特征数据(如初次部署),则从 MySQL 数据库加载。Redis 可作为 内存缓存 + 异步更新机制,保证各节点识别数据一致性
    3. 加载后,内存缓存即可支持毫秒级人脸识别。
  • 实现示例
@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、 特征更新与同步策略

在分布式场景下,用户新增、删除或更新人脸特征时,需要保证所有节点内存保持一致:

  1. 更新顺序

    写内存:立即更新本地内存缓存,保证识别服务实时可用。

    • 写 Redis:同步到 Redis,作为分布式共享层。
    • 写数据库:异步持久化到 MySQL,保证长期数据安全。
  2. 同步机制

    • 利用 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 项目的人脸识别系统可以在保证高性能的同时,实现可扩展、分布式的人脸管理功能,为智能门禁、考勤和访客系统提供稳定支撑。

posted @ 2025-12-11 11:29  feishixin123  阅读(280)  评论(0)    收藏  举报