静かな駅 - 其他
type: channel
link: https://yellowswimming.cn/category/%E5%85%B6%E4%BB%96/
items: Maven Search插件（一键搜索maven依赖）
items: 权限操作：RBAC+ACL搭配
items: 微服务架构-Outbox发件箱模式
items: 设计模式原则分析
items: 观察者模式
items: UML建模语言
items: Docker
items: FAERY: An FPGA-accelerated embedding-based Retrieval System

Maven Search插件（一键搜索maven依赖）
type: item
date: 2026-05-06T21:13:00+08:00
description: Maven Dependency Search 在 VS Code 中快速搜索 Maven 中央仓库，一键将 <dependency> 坐标插入到编辑器光标处。 ✨ 功能特性 🔍 智能搜索 关键词模糊搜索：输入 spring-boot 自动展开匹配 spring-boot-starter-web、spring-boot-starter-parent、spring-boot-autoconfigure 等所有相关依赖 通配符支持：可手动输入 org.springframework* 批量搜索整个 Group 下的所有 Artifact 防抖自动触发：输入停顿 500ms 自动搜索，无需手动按 Enter 🎛 高级搜索 展开高级面板，支持多字段精确组合查询： 字段 说明 Group ID 按 groupId 精确或前缀搜索，如 org.springframework.boot Artifact ID 按 artifactId 精确或前缀搜索，如 spring-boot-starter Packaging 按打包类型过滤：jar / pom / war / aar 每页条数 10 / 20 / 50 条可选 多字段同时填写时以 AND 逻辑组合。 📋 分页浏览 结果列表支持分页，顶部显示 共 N 条，第 X/Y 页 页码按钮导航（超出显示省略号） 上页 / 下页快捷按钮 支持直接跳转到指定页 🔎 详情预览 点击左侧任意搜索结果，右侧面板实时展示： Artifact ID、Group ID 版本选择器（自动加载该 Artifact 的全部历史版本，最多 50 个） Packaging 类型 语法高亮的 <dependency> XML 预览，随版本切换实时更新 ⬇ 一键插入 插入到光标处：将选中版本的 <dependency> XML 片段直接写入编辑器当前光标位置 复制到剪贴板：不打开文件也可快速复制坐标 🚀 快速开始 在插件市场搜索 Maven Dependency Search 打开搜索面板 方式 1： 在任意文件中右键 → 🔍 搜索 Maven 依赖 方式 2： 命令面板（Ctrl+Shift+P）→ 输入 Maven: 搜索 Maven 依赖 📖 使用流程 1. 右键编辑器 → 🔍 搜索 Maven 依赖 ↓ 2. 搜索框输入关键词（或展开高级搜索填写 groupId/artifactId） ↓ 3. 左侧列表展示搜索结果 → 点击某条结果 ↓ 4. 右侧显示详情 → 版本下拉框选择所需版本 ↓ 5. 点击「⬇ 插入到光标处」完成 ⌨️ 键盘快捷键 操作 快捷键 立即搜索 Enter 选择上一条结果 ↑ 选择下一条结果 ↓ 🔧 搜索技巧 场景 推荐输入 找 Spring Boot 全家桶 关键词：spring-boot（自动展开子包） 只找 starter 类包 Artifact ID：spring-boot-starter* 找某公司所有包 Group ID：com.alibaba* 找特定 groupId 下的 pom Group ID：org.springframework.boot，Packaging：pom 找 Jackson 相关 关键词：jackson-databind 📦 插入结果示例 插入后，光标处会写入如下内容： <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.2.5</version> </dependency> 🌐 数据来源 本插件通过 Maven Central REST API 实时检索： https://search.maven.org/solrsearch/select?q=<query>&rows=<n>&start=<offset>&wt=json 数据完全来自 Maven 官方中央仓库，实时准确。 📋 变更日志 请查看 CHANGELOG.md 📄 许可证 MIT

权限操作：RBAC+ACL搭配
type: item
date: 2025-05-04T10:12:37+08:00
description: 权限操作：RBAC+ACL搭配 1. RBAC(基于角色的访问控制) RBAC将权限分配给角色，用户通过关联角色间接获得权限。系统通过“用户-角色-权限”的层级关系实现权限管理。 这种经典的权限控制设计有一个缺点，就是难以处理特殊个体的细粒度权限，比如我作为管理员，我需要将某个文件的访问权限交给不是管理员，但是信任的人(比如张三)进行访问。 2. ACL(访问控制列表) ACL直接在资源上定义允许操作的主体，每条记录明确指定"谁可以对某资源做什么" 这种划分权限类似于我们平常使用的windows文件管理的方式，其主要优势在于灵活性强，每条权限规则都明确可见。但是缺点也很明显，那就是当用户数量或者资源数量很大的时候，ACL条目呈现指数级增长。 3. RBAC 与 ACL 联合使用的优势 灵活性与效率的平衡 RBAC 处理通用权限（如部门角色、职级权限） ACL 处理例外场景（如特殊用户需要访问特定资源） 比如，在企业系统中，普通员工通过角色获得基础权限，但某个机密文件可通过ACL单独授权给少数高管。 减少角色爆炸 通过ACL补充RBAC，避免在RBAC中因为个别的特殊权限去创建冗余角色 精细化权限管理 RBAC管理粗粒度权限（如”访问财务模块“），ACL控制细粒度操作（如”仅能查看报表，不能导出“） 冲突解决与优先级 当RBAC和ACL权限有冲突的时候，可以设定优先级，比如ACL优先于RBAC 4. RBAC + ACL模型举例 RBAC（基于角色的访问控制）经典模型中通常包含以下5张核心表： -- 1. 用户表（3个用户） CREATE TABLE users ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, email VARCHAR(100) ); INSERT INTO users (username, email) VALUES ('alice', 'alice@example.com'), -- admin ('bob', 'bob@example.com'), -- user ('charlie', 'charlie@example.com'); -- user -- 2. 角色表（admin 和 user） CREATE TABLE roles ( role_id INT PRIMARY KEY AUTO_INCREMENT, role_name VARCHAR(50) NOT NULL UNIQUE ); INSERT INTO roles (role_name) VALUES ('admin'), ('user'); -- 3. 权限表（只包含读和写） CREATE TABLE permissions ( permission_id INT PRIMARY KEY AUTO_INCREMENT, permission_key VARCHAR(50) NOT NULL UNIQUE ); INSERT INTO permissions (permission_key) VALUES ('article:read'), ('article:write'); -- 4. 用户-角色关联表 CREATE TABLE user_roles ( user_id INT, role_id INT, PRIMARY KEY (user_id, role_id), FOREIGN KEY (user_id) REFERENCES users(user_id), FOREIGN KEY (role_id) REFERENCES roles(role_id) ); -- 分配角色（1个admin，2个user） INSERT INTO user_roles (user_id, role_id) VALUES (1, 1), -- alice 是 admin (2, 2), -- bob 是 user (3, 2); -- charlie 是 user -- 5. 角色-权限关联表 CREATE TABLE role_permissions ( role_id INT, permission_id INT, PRIMARY KEY (role_id, permission_id), FOREIGN KEY (role_id) REFERENCES roles(role_id), FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ); -- 权限分配：admin有读写，user只有读 INSERT INTO role_permissions (role_id,…

微服务架构-Outbox发件箱模式
type: item
date: 2025-04-25T17:14:16+08:00
description: 微服务架构-Outbox发件箱模式 背景：在现代微服务架构中，服务之间常常需要通过异步消息传递来解耦。例如，一个订单服务创建订单后，需要通知库存服务扣减库存。如何在不同服务间传递事件的同时保持数据一致性，成为了分布式系统设计的核心挑战之一。 本篇将介绍一种经典的一致性设计模式：Outbox（发件箱）模式。它能有效解决“写库成功但消息发送失败”所带来的数据不一致问题。 1. 为什么需要Outbox模式？ 考虑一个场景的场景：订单服务先写入数据库，再发送消息的MQ通知库存服务扣减库存。 public void createOrder(Order order) { orderRepository.save(order); // 写数据库 messageQueue.send("order_created", order); // 发消息 } 如果第2步消息发送失败（比如MQ挂掉了、网络波动），订单已经创建了，但是库存不会减少，就会导致业务不一致，可能这时我们就需要进行回滚或者补偿机制，但是我们这次介绍的Outbox设计模式提供了一个全新的思路，确保在第一次处理时就保证“本地事务+跨服务通信”下的数据一致性问题。 2. Outbox模式架构详解 2.1. 核心思想 在数据库中新增一个outbox表，用于保存待发送的事件。 在本地事务中，同时写入业务数据和outbox事件。 通过后台轮询工具或者定时脚本读取outbox表中的事件，找到状态为待发送的事件，发送消息。 2.2. Outbox模式流程图 3. 代码示例 3.1. 创建订单代码（写入Outbox） @Transactional public void createOrder(Order order) { // 1. 创建订单记录 orderRepository.save(order); // 2. 写入发件箱表 OutboxEvent event = new OutboxEvent("ORDER_CREATED", JsonUtil.toJson(order)); outboxRepository.save(event); } 3.2. 消息发送器（定时轮询版） @Scheduled(fixedDelay = 5000) public void dispatchEvents() { List<OutboxEvent> events = outboxRepository.findPendingEvents(); for (OutboxEvent event : events) { try { messageQueue.send(event.getType(), event.getPayload()); event.markAsSent(); outboxRepository.save(event); } catch (Exception e) { // 这里可以放补偿方法或者最终的回滚机制，也可以放重试机制 } } } 4. 适用场景与总结 Outbox模式适用于： 核心业务链路必须保证事件可靠传递 有本地数据库，且可控制写入事务（如 MYSQL） 可以接受最终一致性，不追求强一致性 不适合： 无本地数据库的场景（如纯计算服务） 要求毫秒级响应的一致性事务 总结 Outbox模式是解决分布式事务一致性的一个经典方法。通过“写数据库 + 发消息”组合的原子化改造，把事件的可靠投递交由后台发送器完成，极大地提升了系统的容错性和可靠性。 在电商订单、支付系统、库存调整等典型的微服务场景中，Outbox都是一个值得考虑的模式。

设计模式原则分析
type: item
date: 2024-12-16T20:53:38+08:00
description: 设计模式原则分析 1. 单一职责原则（Single Responsibility Principle） 1.1 定义 单一职责原则是指一个类应该只有一个责任（即一个功能），一个类应该只负责一项任务。换句话说，如果一个类承担了多个职责，这些职责之间会有不同的变化原因，改变其中某个职责时，可能会影响到其他职责，导致系统更加复杂和难以维护。 1.2 不符合SRP原则举例 假设有一个类同时处理数据存储和报告生成的功能。当存储方式变化时（如从数据库变为文件存储），我们需要修改这个类的存储相关代码；而当报告生成的需求变化时（如格式从 PDF 变为 Excel），我们也需要修改这个类的报告生成代码。此时，这个类有两个职责，它们各自有不同的变化原因，导致这个类的维护变得复杂。 // 违反 SRP 原则：一个类同时负责数据存储和报告生成 public class UserManager { public void saveUser(User user) { // 保存用户到数据库 System.out.println("Saving user to database"); } public void generateUserReport(User user) { // 生成用户报告 System.out.println("Generating report for user"); } } 1.3 遵守SRP原则举例 // 遵守 SRP 原则：将职责分离到不同的类中 public class UserRepository { public void saveUser(User user) { // 保存用户到数据库 System.out.println("Saving user to database"); } } public class UserReportGenerator { public void generateUserReport(User user) { // 生成用户报告 System.out.println("Generating report for user"); } } 在这个例子中，UserRepository 只负责数据存储，而 UserReportGenerator 只负责生成报告。这样，每个类只负责一个功能，系统的职责划分更加清晰，修改某一功能时只需要修改对应的类，而不影响其他部分。 2. 开闭原则（Open/Closed Principle） 2.1 定义 开放-封闭原则指的是软件实体（类、模块、函数等）应该对扩展开放，对修改封闭。这意味着当我们需要为系统添加新功能时，应该通过扩展已有的代码，而不是直接修改已有的代码。 2.2 不符合OCP原则举例 假设我们有一个计算薪水的程序，原本只考虑基本工资和奖金两个因素。如果以后需要加入更多的薪酬计算因素（如补贴、扣款等），如果我们每次都修改原有的计算逻辑，就会违反开放-封闭原则。 // 违反 OCP 原则：修改原有类以支持新的薪资计算方式 public class SalaryCalculator { public double calculateSalary(Employee employee) { // 计算基本工资和奖金 return employee.getBaseSalary() + employee.getBonus(); } } 假设现在我们需要新增一个“补贴”因素，每次添加新因素时都需要修改 SalaryCalculator 类的 calculateSalary 方法。随着需求变化，SalaryCalculator 类会变得越来越复杂，而且容易出错。这就是违反了开放-封闭原则。 2.3 符合OCP原则举例 为了遵守开放-封闭原则，我们可以引入抽象类或接口，将不同的薪资计算策略封装成不同的类，而不是修改 SalaryCalculator 类。 // 遵守 OCP 原则：将薪资计算逻辑通过策略模式封装，便于扩展 public interface SalaryCalculatorStrategy { double calculateSalary(Employee employee); } public class BaseSalaryAndBonusCalculator implements SalaryCalculatorStrategy { @Override public double calculateSalary(Employee employee) { // 计算基本工资和奖金 return employee.getBaseSalary() + employee.getBonus(); } } public class BaseSalaryBon…

观察者模式
type: item
date: 2024-10-10T22:08:26+08:00
description: 观察者模式 观察者模式定义了一种一对多的依赖关系。当一个对象的状态发生变化时，所有依赖于它的对象都会收到通知并自动更新。它通常用于事件处理系统，允许对象之间实现松耦合，便于扩展和维护。 观察者模式的结构 Subject（主题/被观察者）：管理所有的观察者对象，提供注册和删除观察者的方法。当自身状态改变时，它负责通知所有观察者。 Observer（观察者）：定义一个更新接口，用于接收来自被观察者的通知。 ConcreteSubject（具体主题）：具体的被观察者对象，状态发生变化时通知观察者。 ConcreteObserver（具体观察者）：具体的观察者对象，实现了观察者接口，并在接收到通知时做出反应。 举例 假设我们有一个股票市场系统，股票价格变动时需要通知所有的投资者。我们可以用观察者模式来实现这个场景。 package com.example.demo.demos.test; import java.util.ArrayList; import java.util.List; public class test01 { public static void main(String[] args) { Stock stock = new Stock(); Investor investor1 = new Investor("Alice"); Investor investor2 = new Investor("Bob"); Investor investor3 = new Investor("Charlie"); stock.registerObserver(investor1); stock.registerObserver(investor2); stock.registerObserver(investor3); stock.setPrice(120.0f); stock.removeObserver(investor2); stock.setPrice(130.0f); } } interface Observer{ void update(float price); } interface Subject{ void registerObserver(Observer o); void removeObserver(Observer o); void notifyObservers(); } class Stock implements Subject{ private List<Observer> observers ; private float price; public Stock(){ observers = new ArrayList<>(); } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { observers.remove(o); } @Override public void notifyObservers() { for(Observer o: observers){ o.update(price); } } public void setPrice(float price){ this.price = price; notifyObservers(); } } class Investor implements Observer{ private String name; public Investor(String name){ this.name = name; } @Override public void update(float price) { System.out.printf("%s: The stock price has changed to %.2f%n", name, price); } } 输出结果如下： Alice: The stock price has changed to 120.00 Bob: The stock price has changed to 120.00 Charlie: The stock price has changed to 120.00 Alice: The stock price has changed to 130.00 Charlie: The stock price has changed to 130.00

UML建模语言
type: item
date: 2023-02-15T17:28:00+08:00
description: UML建模语言 1. UML概述 ​ UML(Unified Modeling Language) 是软件界第一个统一的建模语言，该方法结合了Booch，OMT,和OOSE方法的优点，统一了符号体系，并从其他的方法和工程实践中吸收了许多经过实际检验的概念和技术 ​ 他是一种标准的表示，已成为国际软件界广泛承认的标准。他是第三代面向对象的开发方法，是一种基于面向对象的可视化的通用建模语言。为不同领域的用户提供了统一的交流标准--UML图 ​ UML应用领域很广泛，可用于软件开发建模的各个阶段，商业建模，也可用于其他类型的系统 1.1 什么模型？为什么要建模？ ​ 模型是一个系统的完整的抽象。人们对某个领域特定问题的求解及解决方案，对他们的理解和认识都蕴含在模型中。 ​ 通常，开发一个计算机系统是为了解决某个领域的特定问题，问题的求解过程，就是领域问题到计算机系统的映射。 UML作为一种可视化的建模语言，提供了丰富的基于面向对象概念的模型元素及其图形表示元素 1.2 UML的形成 九十年代中，面向对象方法已经成为软件分析和设计方法的主流。 1994年10月Jim Rumbaugh和Grady Booch共同合作把他们的OMT和Booch方法统一起来，到1995年成为“统一方法”(Unified Method）版本0.8。随后，IvarJacobson加入，并采用他的用例(User case)思想,到1996年，成为“统一建模语言”版本0.9。 1997年1月，UMIL版本1.0被提交给OMG组织，作为软件建模语言标准的候选。其后的半年多时间里，一些重要的软件开发商和系统集成商都成为“UML伙伴”，如IBM,Mircrosoft,HP等.1997年11月7日被正式采纳作为业界标准。 1.3 UML的主要内容 UML的定义包括UML语义和UML表示法两个部分 （1）UML语义 描述基于UML的精确元模型定义。元模型为UML的所有元素在语法和语义上提供了简单、一致、通用的定义性说明，使开发者能在语义上取得一致，消除了因人而异的表达方法所造成的影响。此外UML还支持对元模型的扩展定义。 UML支持各种类型的语义。如布尔、表达式、列表、阶、名字、坐标、字符串和时间等，还允许用户自定义类型。 （2）UML表示法 定义UML符号的表示法，为开发者或开发工具使用这些图形符号和文本语法为系统建模提供了标准。这些图形符号和文字所表达的是应用级的模型，在语义上它是UML元模型的实例 1.4 视图 一个系统应从不同的角度进行描述，从一个角度观察到的系统称为视图。 视图由多个图构成，他不是一个图表，而是在某一个抽象层上，对系统的抽象表示。 如果要为系统建立一个完整的模型图，需定义一定数量的视图，每个视图表示系统的一个特殊方面。另外，视图还把建模语言和系统开发时选择的方法或过程连接起来。 1.5 图 UML语言定义了5种类型和9种不同的图，把他们有机的结合起来就可以描述系统的所有视图。 用例图（user case diagram）从用户角度描述系统功能，并指出各功能得操作者 静态图（static diagram），表示系统的静态结构。包括类图、对象图、包图 行为图（behavior diagram），描述系统的动态模型和组成对象间的交互关系。包括状态图、活动图 交互图（interactive diagram），描述对象间的交互关系。包括顺序图、合作图 实现图（implementation diagram）用于描述系统的物理实现。包括构件图、部署图 1.6 模型元素 代表面向对象中的类，对象，关系和消息等概念，是构成图的最基本的常用的元素。一个模型元素可以用在多个不同的图中，无论怎样使用，他总是具有相同的含义和相同的符号表示。 1.7 通用机制 用于表示其他信息，比如注释，模型元素的语义等。 另外，为了适应用户的需求，他还提供了扩展机制，包括构造型、标记值和约束。使用UML语言能够适应一个特殊的方法（或过程），或扩充至一个组织或用户 1.8 UML的特点 （1）统一标准：UML统一了Booch、OMT和OOSE等方法中的基本概念，已成为OMG的正式标准，提供了标准的面向对象的模型元素的定义和表示。 （2）面向对象：UML还吸取了面向对象技术领域中的其他流派的长处。UML符号表示考虑了各种方法的图形表示，删掉了大量易引起混乱的、多余的和极少使用的符号，也添加了一些新符号。 （3）可视化、表示能力强 系统的逻辑模型或实现模型都能用UML模型清晰的表示，可用于复杂软件系统的建模 （4）独立于过程：UML是系统建模语言，独立于开发过程 2. 通用模型元素 模型元素是UML构造系统的各种元素，是UML构建模型的基本单位。模型元素代表面向对象中的类，对象，关系和消息等概念…

Docker
type: item
date: 2023-02-04T15:53:00+08:00
description: Docker 1. docker概述 docker三件套：镜像，容器，仓库 容器与镜像的关系类似于面向对象编程中的对象与类 Docker 面向对象 容器 对象 镜像 类 从面向对象角度 Docker利用容器独立运行的一个或一组应用，应用程序或服务运行在容器里面，容器就类似于一个虚拟化的运行环境，容器是用镜像创建的运行实例。就像是Java中的类和实例对象一样，镜像是静态的定义，容器是镜像运行时的实体。容器为镜像提供了一个标准的和隔离的运行环境，它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。 从镜像容器的角度 可以把容器看做是一个简易版的Linux环境（包括root用户权限、进程空间、用户空间和网络空间等）和运行在其中的应用程序 关于docker安装可自行上CSDN搜索，比较简单。注意配置国内镜像仓库 2. 为什么docker会比虚拟机快 docker有着比虚拟机更少的抽象层 由于docker不需要Hypervisor实现硬件资源虚拟化，运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显的优势 docker利用的是宿主机内核，而不需要加载操作系统OS的内核 当新建一个容器时，docker不需要和虚拟机一样重新加载一个操作系统内核。进而避免引寻、加载操作系统内核返回等比较费时费资源的过程，当新建一个虚拟机时，虚拟机软件需要加载OS，返回新建过程是分钟级。而docker由于直接利用宿主机操作系统，则省略了返回过程，因此新建一个docker容器只需要几秒钟 Docker容器 虚拟机（VM） 操作系统 与宿主机共享OS 宿主机OS上运行虚拟机OS 存储大小 镜像小，便于存储与传输 镜像庞大（vmdk、vdi等） 运行性能 几乎无额外性能损失 操作系统额外的CPU、内存消耗 移植性 轻便、灵活、适应于Linux 笨重，与虚拟化技术耦合度高 硬件亲和性 面向软件开发者 面向硬件运维者 部署速度 快速，秒级 较慢，10s以上 3. Docker常用命令 3.1 帮助启动类命令 启动docker：systemctl start docker 停止docker： systemctl stop docker 重启docker：systemctl restart docker 查看docker状态 systemctl status docker 开机启动：systemctl enable docker 查看docker概要信息：docker info 查看docker总体帮助文档：docker --help 查看docker命令帮助文档： docker具体命令 --help 3.2 镜像命令 docker images-》 列出本地主机上的镜像 docker search 镜像名 docker pull 镜像名 docker system df-》 查看镜像/容器/数据卷所占的空间 docker rmi 镜像ID ps：仓库名和标签名都是None的镜像称为虚悬镜像 3.3 容器命令 只要记住--help现查就可以 本次以Ubuntu镜像演示 启动ubuntu docker run -it ubuntu bash 参数说明： -i ：交互式操作 -t： 终端 Ubuntu：Ubuntu镜像 bash：放在镜像名后的是命令，这里我们希望有个交互式Shell，因此用bash（/bin/bash） exit退出终端（ctrl+p+q退出不会停止容器） 列出正在运行的容器 docker ps 重启容器 docker restart容器ID或容器名 停止容器 docker stop 容器ID或容器名 强制停止容器 docker kill 容器ID或容器名 启动已停止的容器 docker start 容器ID或容器名 启动守护式容器（后台服务器） 说明：Docker容器后台运行，就必须有一个前台进程。容器运行的命令如果不是那些一直挂起的命令（比如top，tail），就会自动退出（交互式容器），如 docker run -d ubuntu ​ 我们可以看到虽然启动了一个容器，但是没有正在运行的容器，代表在启动一瞬间就自己退出了。 ​ 下面用redis前后台启动演示case ​ 前台交互式启动 docker run -it redis:latest 后台守护式启动 docker run -d redis ​ 此时并没有自动退出，因为redis是守护式容器 ​ 可以通过docker logs+容器ID查看前台输出 查看容器内运行的进程 docker top 容器ID 查看容器内部细节 docker inspect 容器ID 进入后台容器并以命令行交互 docker exec …

FAERY: An FPGA-accelerated embedding-based Retrieval System
type: item
date: 2022-11-01T19:58:00+08:00
description: FAERY: An FPGA-accelerated embedding-based Retrieval System 参考文献 参考文献 1. 摘要 基于嵌入的检索（EBR)广泛用于推荐系统，以从包含百万个或者更多项目的大型语料库中检索数千个相关候选项。一个好的EBR系统需要同时实现高通量和低延迟，因为高吞吐量意味着节省成本，而低延迟可以改善用户体验.不幸的是，现有的基于CPU和GPU的的EBR的性能由于其固有的架构限制而远非最佳。在下文中，我将首先介绍一个理想而实用的EBR系统是如何工作的，然后基于ideal状况，人们提出并设计的FPGA加速的EBR-FAERY，它实现了实用的理想EBR系统的最佳性能。FAERY由三个关键组件组成：它使用高带宽HBM进行内存带宽密集型语料库扫描，使用数据并行方法进行相似度计算，以及基于管道的方法进行K选择。为了进一步减小硬件资源，FAERY引入了一个过滤器来提前丢弃非Top-K项目。实验表明，在GPU相同显存带宽的情况下，降级后在目标为10ms的延迟目标下，与基于GPU的EBR相比，FAERY仍能达到1.21~12倍延迟低于其以及4.29倍吞吐率高于其的目标。 2. 介绍 2.1 检索与排序 推荐系统已经在许多在线服务中得到广泛采用。为了从包含数百万候选项目的大型语料库中进行推荐，工业大规模推荐系统通常分为两层，即检索和排序。如图所示 （图1) 检索通过简单的算法从庞大的语料库中快速选择数千个相关项目，而排序则利用复杂的算法对检索结果进行更精确的排序，然后从排序的项目中选择数十个。 现实世界的检索系统进行多通道检索：他利用不同通道中的不同策略来检索不同的候选者，然后将其合并和过滤以生成最终的检索结果。在多通道检索策略中，基于嵌入的检索（EBR) 越来越受欢迎。 2.2 EBR检索大致流程 （图2) 具体来说，如图1所示，EBR算法涉及评分，它扫描语料库以获取所有项目并计算每个项目嵌入和给定查询嵌入之间的相似度分数(例如，通过内积），以及 K-selection ,它根据相似度得分返回Top-K项目。返回的 EBR的Top-K项通常被排序，以简化来自多个通道的合并和过滤检索候选。这种EBR系统的性能很重要。一方面，增加每台EBR服务器的吞吐量会降低整体服务器成本，因为只需要更少的服务器来提供目标每秒查询数(QPS)。另一方面，降低每个EBR服务器的延迟会减少检索时间，这既可以缩短用户的整体等待时间，也可以为排名计算留出更多时间以获得更好的推荐结果[11]。因此，延迟猎杀的吞吐量成为EBR系统的关键指标。 2.3 理想架构 为了实现高延迟限制吞吐量，我们对图1中所示的EBR算法进行了表征，并派生了一个实际理想的EBR硬件架构。具体来说，语料库扫描是一种内存密集型操作，需要大的外部内存容量和高内存带宽。相似度计算和K选择都是计算密集型的。它们应该将内存带宽与跨多个算子实例的数据并行架构相匹配。此外，为了与步骤或操作符之间的计算重叠通信，在K-selection内部和整个EBR数据流中都需要管道并行性。然后，我们通过在批量查询之间共享语料库扫描并提供单独的计算管道来扩展理想架构以支持批量查询.因此，理想的架构实现了最佳的查询延迟，并随着批量大小线性扩展延迟限制的吞吐量 2.4 现状 通过将现有的基于CPU和GPU的EBR与理想架构进行比较，我们意识到，不幸的是，由于其固有的架构限制，现有方法都没有实现最佳性能。首先,尽管内存容量很大，但由于内存带宽低，CPU在语料库扫描中表现不佳，并且由于内核数量有限，无法同时很好地支持所需的并行范式。其次，虽然GPU为数据并行提供了更高的内存带宽和海量计算核心，但由于明确的资源边界，GPU并未针对K-selection和整个EBR 数据流所需的管道并行进行优化。 我们观察到FPGA 是一种可编程硬件设备,在一些超大规模云提供商中很容易获得，它具有实际理想EBR架构的所有所需特性。一些现代FPGA配备了大容量高带宽存储器(HBM)，非常适合语料库扫描。此外，FPGA 提供足够的片上存储器和完全可编程的计算元件，以便为各种操作员启用适当的并行范式。 2.5 FAERY的优势 FAERY ，这是一种基于FPGA加速嵌入的检索系统，它是理想EBR架构的体现，并实现了高性能。具体来说，FAERY将语料库存储在FPGA的HBM中，为内存带宽密集型的语料库扫描提供了高带宽。FAERY利用语料库管理器在运行时最大化HBM带宽利用率，同时保留内存高效存储并启用在线语料库更新。FAERY遵循理想架构设计数据并行性的相似性计算和管道并行性的K-selection。与理想架构不同的是，FAERY只需要一个K-selection流水线，并根据在K-selection流水线中观察到的独特属…