当前位置: 首页 > news >正文

数据库主键设计

文章目录

  • 前言
      • 1. 自增ID(Auto-Increment)
      • 2. GUID (Globally Unique Identifier)
      • 3. 雪花算法(Snowflake)
      • 处理时钟回拨的方法
        • 1. 简单等待
        • 2. 配置时钟回拨安全窗口
        • 3. 使用不同的机器 ID
      • 小结
      • 稳定的雪花算法实现方案
      • 示例实现
        • 1. 定义雪花算法类
        • 2. 使用 Redis 或数据库实现分布式唯一 ID
      • 解释
      • 小结
      • 其他方法
      • 总结


前言

数据库主键的设计是数据库架构中的一个重要环节,不同的主键生成策略适用于不同的场景和需求


以下是几种常见的主键设计方法及其优缺点比较:

1. 自增ID(Auto-Increment)

优点:

  • 实现简单,数据库自动管理,无需开发者介入。
  • 递增的特性使得数据插入速度快,因为插入总是发生在索引的末尾。
  • 易于理解和使用,便于查询和排序。

缺点:

  • 分布式系统中难以保证全局唯一,因为每个节点的计数器独立增长。
  • 数据泄露风险,自增ID容易暴露数据库的规模和增长速度。
  • 如果发生大量删除操作,可能导致主键ID不连续,影响美观但不影响功能。

2. GUID (Globally Unique Identifier)

优点:

  • 全球唯一,无论在任何系统、任何地点生成,都能保证唯一性。
  • 无需依赖数据库,可以在客户端生成,适合分布式系统。
  • 支持提前生成ID,有利于并行处理和离线操作。

缺点:

  • 长度较大(通常为32字符),占用更多的存储空间和索引空间。
  • 无序的特性可能导致索引碎片,降低插入性能。
  • 不易读,不便于人工识别和调试。

3. 雪花算法(Snowflake)

雪花算法(Snowflake Algorithm)是一种用于生成唯一ID的算法,最初由Twitter公司开发。它是为了解决分布式系统中生成全局唯一ID的需求而设计的。在分布式系统中,如果不同节点生成的ID可能会发生冲突,这就需要一种机制来保证生成的ID在整个系统中唯一。

雪花算法的设计考虑了以下几个因素:

  1. 时间戳(Timestamp):使用当前时间来确保生成的ID是递增的,这样可以保证生成的ID是有序的。
  2. 机器ID(Machine ID):将机器的唯一标识(比如机器的MAC地址)作为一部分ID,确保不同机器生成的ID不会冲突。
  3. 序列号(Sequence Number):用来解决同一毫秒内生成多个ID时的冲突问题。

Java中如何使用雪花算法来设计数据库主键呢?下面是一个简单的示例:

public class SnowflakeIdGenerator {// 定义机器ID,可以通过配置文件或其他方式设置private long machineId;// 定义序列号private long sequence = 0L;// 定义初始时间戳private long twepoch = 1622874000000L; // 2021-06-05 00:00:00// 定义各部分占位数private long machineIdBits = 5L;private long maxMachineId = -1L ^ (-1L << machineIdBits);private long sequenceBits = 12L;private long sequenceMask = -1L ^ (-1L << sequenceBits);// 定义机器ID左移位数private long machineIdShift = sequenceBits;// 定义时间戳左移位数private long timestampLeftShift = sequenceBits + machineIdBits;// 上次生成ID的时间戳private long lastTimestamp = -1L;public SnowflakeIdGenerator(long machineId) {if (machineId > maxMachineId || machineId < 0) {throw new IllegalArgumentException("Machine ID can't be greater than " + maxMachineId + " or less than 0");}this.machineId = machineId;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");}if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 当同一毫秒内的序列号超过上限时,等待下一毫秒timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;return ((timestamp - twepoch) << timestampLeftShift) | (machineId << machineIdShift) | sequence;}private long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}public static void main(String[] args) {SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1); // 传入机器IDfor (int i = 0; i < 10; i++) {System.out.println(idGenerator.nextId());}}
}

在这个示例中,我们通过nextId()方法来生成雪花算法生成的唯一ID。首先,我们需要设置一个机器ID,确保不同的机器有不同的ID。然后,调用nextId()方法即可生成一个唯一的ID,这个ID包含了时间戳、机器ID和序列号三部分。最后,我们可以将生成的ID作为数据库表的主键。

值得注意的是,雪花算法生成的ID是趋势递增的,因此在数据库中使用时可能会带来一定的优势,比如辅助索引的性能优化。但也要注意在高并发情况下可能出现的一些问题,比如时钟回拨等。

优点:

  • 结合了自增ID和GUID的优点,生成的ID是趋势递增的,且全局唯一。
  • 高性能,适用于分布式环境,能够按需分配workerId和数据中心id,保证唯一性。
  • ID较短(一般为64位),相比GUID节省存储空间。
  • 有序性有助于索引优化。

缺点:

  • 需要一个中心节点(或者多个,但需要协调)来生成ID,有一定的运维成本。
  • 时钟回拨问题可能会影响ID的生成,需要特殊处理。

然而,雪花算法依赖于时间戳,因此时钟回拨(clock rollback)会对其造成问题。

处理时钟回拨的方法

1. 简单等待

当检测到时钟回拨时,直接等待直到时间回到正确的时间。这是最简单的处理方式,但会导致 ID 生成暂停一段时间。

public class SnowflakeIdGenerator {private long lastTimestamp = -1L;public synchronized long nextId() {long timestamp = timeGen();// 如果当前时间小于上一次生成ID的时间戳,说明系统时钟回拨if (timestamp < lastTimestamp) {// 等待直到时钟追上while (timestamp < lastTimestamp) {timestamp = timeGen();}}lastTimestamp = timestamp;return generateId(timestamp);}private long timeGen() {return System.currentTimeMillis();}private long generateId(long timestamp) {// 生成ID的逻辑return timestamp;}
}
2. 配置时钟回拨安全窗口

允许一定范围内的时钟回拨,在这个范围内继续生成 ID,但如果超出这个范围则抛出异常或采取其他措施。

public class SnowflakeIdGenerator {private long lastTimestamp = -1L;private static final long MAX_BACKWARD_MS = 5L; // 允许的最大时钟回拨时间public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {long offset = lastTimestamp - timestamp;if (offset <= MAX_BACKWARD_MS) {// 等待,直到时钟追上try {Thread.sleep(offset + 1);} catch (InterruptedException e) {throw new RuntimeException(e);}timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards. Refusing to generate id");}} else {throw new RuntimeException("Clock moved backwards. Refusing to generate id");}}lastTimestamp = timestamp;return generateId(timestamp);}private long timeGen() {return System.currentTimeMillis();}private long generateId(long timestamp) {// 生成ID的逻辑return timestamp;}
}
3. 使用不同的机器 ID

在分布式系统中,每台机器有唯一的机器 ID。当检测到时钟回拨时,改变机器 ID 来避免冲突。这种方法需要协调机器 ID 的分配。

public class SnowflakeIdGenerator {private long lastTimestamp = -1L;private long machineId;private static final long MAX_MACHINE_ID = 1023L;public SnowflakeIdGenerator(long machineId) {if (machineId < 0 || machineId > MAX_MACHINE_ID) {throw new IllegalArgumentException("Machine ID out of range");}this.machineId = machineId;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {machineId = (machineId + 1) & MAX_MACHINE_ID;if (machineId == 0) {// 如果机器ID回到0,说明时钟回拨过大,拒绝生成IDthrow new RuntimeException("Clock moved backwards. Refusing to generate id");}timestamp = timeGen();}lastTimestamp = timestamp;return generateId(timestamp, machineId);}private long timeGen() {return System.currentTimeMillis();}private long generateId(long timestamp, long machineId) {// 生成ID的逻辑,包含时间戳和机器IDreturn (timestamp << 22) | (machineId << 12);}
}

小结

  1. 简单等待:当检测到时钟回拨时,等待直到时钟恢复到正确时间。这种方法简单但会导致 ID 生成暂停。
  2. 时钟回拨安全窗口:允许一定范围内的时钟回拨,如果超出这个范围则抛出异常或采取其他措施。
  3. 不同的机器 ID:当检测到时钟回拨时,改变机器 ID 来避免冲突。这种方法需要协调机器 ID 的分配。

稳定的雪花算法实现方案

以下是一个经过优化的方案,涵盖时钟回拨问题、分布式系统中的唯一性问题和高可用性问题

  1. 机器 ID 和数据中心 ID:通过配置不同的机器 ID 和数据中心 ID 来确保分布式系统中的唯一性。
  2. 时钟回拨处理:使用递增序列和缓存的时间戳来处理时钟回拨问题。
  3. 高可用性:结合 Redis 或数据库来生成分布式唯一 ID。

示例实现

1. 定义雪花算法类
public class SnowflakeIdGenerator {private static final long EPOCH = 1609459200000L; // 自定义纪元时间(2021-01-01)private static final long DATA_CENTER_ID_BITS = 5L;private static final long MACHINE_ID_BITS = 5L;private static final long SEQUENCE_BITS = 12L;private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS + DATA_CENTER_ID_BITS;private final long dataCenterId;private final long machineId;private long sequence = 0L;private long lastTimestamp = -1L;public SnowflakeIdGenerator(long dataCenterId, long machineId) {if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {throw new IllegalArgumentException(String.format("DataCenter ID can't be greater than %d or less than 0", MAX_DATA_CENTER_ID));}if (machineId > MAX_MACHINE_ID || machineId < 0) {throw new IllegalArgumentException(String.format("Machine ID can't be greater than %d or less than 0", MAX_MACHINE_ID));}this.dataCenterId = dataCenterId;this.machineId = machineId;}public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");}if (timestamp == lastTimestamp) {sequence = (sequence + 1) & MAX_SEQUENCE;if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;return ((timestamp - EPOCH) << TIMESTAMP_SHIFT)| (dataCenterId << DATA_CENTER_ID_SHIFT)| (machineId << MACHINE_ID_SHIFT)| sequence;}private long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}
}
2. 使用 Redis 或数据库实现分布式唯一 ID

为了进一步提高高可用性和唯一性,可以结合 Redis 或数据库实现分布式唯一 ID 生成。这里是一个使用 Redis 的示例:

import redis.clients.jedis.Jedis;public class DistributedIdGenerator {private final SnowflakeIdGenerator snowflakeIdGenerator;private final Jedis jedis;public DistributedIdGenerator(long dataCenterId, long machineId, String redisHost, int redisPort) {this.snowflakeIdGenerator = new SnowflakeIdGenerator(dataCenterId, machineId);this.jedis = new Jedis(redisHost, redisPort);}public long nextId() {long id = snowflakeIdGenerator.nextId();String key = "snowflake:" + id;while (jedis.exists(key)) {id = snowflakeIdGenerator.nextId();key = "snowflake:" + id;}jedis.setex(key, 3600, "1"); // 设置过期时间,避免长期存储return id;}
}

解释

  1. 基本雪花算法

    • EPOCH:自定义的纪元时间。
    • DATA_CENTER_ID_BITSMACHINE_ID_BITSSEQUENCE_BITS:数据中心 ID、机器 ID 和序列号的位数。
    • nextId 方法:生成唯一 ID,并处理时钟回拨问题。
  2. 分布式唯一 ID

    • 使用 Redis 确保 ID 唯一性:在生成 ID 后,将其存储在 Redis 中,检查是否重复。
    • jedis.setex(key, 3600, "1"):使用带过期时间的键来避免长期存储。
  3. 时钟回拨处理

    • 当检测到时钟回拨时,抛出异常或等待时间前进。
    • 使用 tilNextMillis 方法等待直到时间前进。

小结

这种方案结合了雪花算法的高性能和 Redis 的分布式存储能力,解决了时钟回拨问题,并确保在分布式环境下生成唯一 ID。通过这些措施,可以实现一个稳定、高效的分布式唯一 ID 生成系统。

其他方法

  • 复合主键:结合多个字段作为主键,适用于表中没有自然唯一标识符的场景。但增加了查询和维护的复杂性。
  • 业务相关ID:如订单号,易于理解且与业务紧密相关,但可能需要额外的逻辑来保证唯一性,且扩展性较差。

总结

选择哪种主键生成策略取决于具体的应用场景:

  • 对于单体应用或简单的分布式系统,自增ID可能是最简单高效的选择。
  • 在分布式系统中,尤其是跨多个数据中心时,雪花算法因其高性能和全局唯一性成为优选。
  • 当全局唯一性是首要考虑因素,且对存储空间不太敏感时,GUID是合适的选择。
  • 具体场景下,也可以根据业务需求考虑复合主键或业务相关ID的方案。

相关文章:

数据库主键设计

文章目录 前言1. 自增ID&#xff08;Auto-Increment&#xff09;2. GUID (Globally Unique Identifier)3. 雪花算法&#xff08;Snowflake&#xff09;处理时钟回拨的方法1. 简单等待2. 配置时钟回拨安全窗口3. 使用不同的机器 ID 小结稳定的雪花算法实现方案示例实现1. 定义雪…...

小熊家务帮day13-day14 门户管理(ES搜索,Canal+MQ同步,索引同步)

目录 1 服务搜索1.1 需求分析1.2 技术方案1.2.1 使用Elasticsearch进行全文检索&#xff08;为什么数据没有那么多还要用ES&#xff1f;&#xff09;1.2.2 索引同步方案1.2.2.1 Canal介绍1.2.2.1 Canal工作原理 1 服务搜索 1.1 需求分析 服务搜索的入口有两处&#xff1a; 在…...

Android8.1高通平台修改默认输入法

需求 安卓8.1 SDK原生的输入法只能打英文, 需要替换成中文输入法. 以高通平台为例, 其它平台也适用. 查看设备当前默认输入法 adb shell settings list secure | grep input 可以看到当前默认是LatinIME这个安卓原生输入法. default_input_methodcom.android.inputmethod.l…...

49. 字母异位词分组

思路&#xff1a;题目的意思是&#xff0c;将所有字母相同的字符串放到一个数组中 解题思路是&#xff1a;使用map,使用排序好的字符串作为key&#xff0c;源字符串作为value,就可以实现所有字母相同的字符串对应一个key vector<vector<string>> groupAnagrams(ve…...

负压实验室设计建设方案

随着全球公共卫生事件的频发&#xff0c;负压实验室的设计和建设在医疗机构中的重要性日益凸显。负压实验室&#xff0c;特别是负压隔离病房&#xff0c;主要用于控制传染性疾病的扩散&#xff0c;保护医护人员和周围环境的安全。广州实验室装修公司中壹联凭借丰富的实验室装修…...

作文笔记10 复述故事

一、梳理内容&#xff08;用表格&#xff0c;示意图&#xff09; 救白蛇 得宝石 救相亲 变石头 人们纪念海力布 二、按顺序&#xff0c;不遗漏主要情节 &#xff08;猎人海力布热心救人&#xff09;救白蛇 得宝石&#xff08;白蛇强调宝石禁忌&#xff09;&#xff08;海力…...

业务安全蓝军测评标准解读—业务安全体系化

目录 1.前言 2.业务蓝军测评标准 2.1 业务安全脆弱性评分(ISVS) 2.2 ISVS评分的参考意义<...

关于焊点检测SJ-BIST)模块实现

关于焊点检测SJ-BIST&#xff09;模块实现 语言 &#xff1a;Verilg HDL 、VHDL EDA工具&#xff1a;ISE、Vivado、Quartus II 关于焊点检测SJ-BIST&#xff09;模块实现一、引言二、焊点检测功能的实现方法&#xff08;1&#xff09; 输入接口&#xff08;2&#xff09; 输出接…...

使用 Logback.xml 配置文件输出日志信息

官方链接&#xff1a;Chapter 3: Configurationhttps://logback.qos.ch/manual/configuration.html 配置使用 logback 的方式有很多种&#xff0c;而使用配置文件是较为简单的一种方式&#xff0c;下述就是简单描述一个 logback 配置文件基本的配置项&#xff1a; 由于 logba…...

Allegro-开店指南

开店指南 Allegro企业账户注册流程 Allegro注册流程分成两个主要阶段: 第一创建您的账户&#xff0c;第二激活您账户的销售功能。完成两个阶段&#xff0c;才能在Allegro进行销售。 中国企业应该入驻Business account&#xff08;企业账户&#xff09;。 第二阶段&#xff…...

Spring AI 第二讲 之 Chat Model API 第二节Ollama Chat

通过 Ollama&#xff0c;您可以在本地运行各种大型语言模型 (LLM)&#xff0c;并从中生成文本。Spring AI 通过 OllamaChatModel 支持 Ollama 文本生成。 先决条件 首先需要在本地计算机上运行 Ollama。请参阅官方 Ollama 项目 README&#xff0c;开始在本地计算机上运行模型…...

服务器环境搭建

服务器的使用。 本地服务器 虚拟机服务器 云服务器。 服务器配置内容 如何实现部署到云服务器&#xff1f; 环境部署是一件费劲的事。 自己一个人坚持慢慢弄&#xff0c;也能行。 但是要是一个组的人&#xff0c;问你怎么弄环境。 可就难了&#xff0c;不同的人部署的环境不同&…...

数仓建模—指标体系指标拆解和选取

数仓建模—指标拆解和选取 第一节指标体系初识介绍了什么是指标体系 第二节指标体系分类分级和评价管理介绍了指标体系管理相关的,也就是指标体系的分级分类 这一节我们看一下指标体系的拆解和指标选取,这里我们先说指标选取,其实在整个企业的数字化建设过程中我们其实最…...

微信小程序如何在公共组件中改变某一个页面的属性值

需求 公共组件A改变页面B的属性isShow的值。 思路 首先目前我不了解可以直接在组件中改变页面的值的方法&#xff0c;所以我通过监听的方式在B页面监听app.js的某一属性值的改变从而改变B页面的值&#xff0c;众所周知app.js的某一属性值是很容易就能更改的。 app.js globa…...

TCP/UDP的区别

首先来介绍一下什么是TCP和UDP TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是互联网协议套件中两个重要的传输层协议。它们在数据传输的方式、可靠性、连接性等方面有显著的区别。 总之他们两个就是个协议&#xff0c;协议也就是数…...

JavaWeb1 Json+BOM+DOM+事件监听

JS对象-Json //Json 字符串转JS对象 var jsObject Json.parse(userStr); //JS对象转JSON字符串 var jsonStr JSON.stringify(jsObject);JS对象-BOM BOM是浏览器对象模型&#xff0c;允许JS与浏览器对话 它包括5个对象&#xff1a;window、document、navigator、screen、hi…...

DSP6657 GPIO中断学习(只支持GPIO0-15)

1 简介 使用创龙板卡的KEY2按键通过中断的方式控制LED3的亮灭 2 中断学习 在C665x设备上&#xff0c;CPU中断是通过C66x CorePac中断控制器进行配置的。该中断控制器允许最多128个系统事件被编程到任意12个CPU可屏蔽中断输入&#xff08;CPUINT4至CPUINT15&#xff09;、CPU…...

vue数字翻盘,翻转效果

数字翻转的效果 实现数字翻转的效果上面为出来的样子 下面为代码&#xff0c;使用的时候直接引入&#xff0c;还有就是把图片的路径自己换成自己或者先用颜色替代&#xff0c;传入num和numlength即可 <template><div v-for"(item, index) in processedNums&quo…...

【简单讲解TalkingData的数据统计】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…...

JMeter的基本使用

JMeter的基本使用三步骤&#xff1a;1.添加线程、2.添加请求、3.添加查询结果的内容 如果需要添加token请求头来验证&#xff0c;则需要再加上一步骤&#xff1a;添加请求头 1.线程 添加线程的方式 主要修改者三个属性值 Number of Threads&#xff1a;并发线程数 Ramp-up…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...