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

Bitmap在数仓中的应用

一、背景

在数据仓库的日常工作中,我们经常需要面对海量数据的存储和高效查询问题。尤其是,当业务对性能的要求越来越高、数据量持续增长时,传统的处理方式往往显得笨拙而低效。而这时候,Bitmap(位图)作为一种“看似简单却威力强大”的数据结构,逐渐展现出它的价值。简单来说,Bitmap 就像是一种用“0”和“1”记录信息的小工具。它通过位的形式将数据高效地压缩存储,同时支持快速的集合运算,比如并集、交集、补集等。这些特性使得 Bitmap 在高基数去重、行为统计、快速筛选等场景中表现得尤为出色。比如,给定一个数千万级别的用户群,使用 Bitmap 技术可以在几毫秒内完成某些复杂的查询操作,这在传统方法中可能需要数十秒甚至更长时间。
Bitmap 在数据仓库中的应用非常广泛。无论是帮助优化指标计算,还是提升查询性能,它都能够为开发者解决很多头疼的问题。例如,在广告投放场景下,我们需要实时统计某一人群的曝光次数;在用户运营中,我们可能需要高效筛选出符合特定标签的用户群。这些看似复杂的任务,通过 Bitmap,往往能以优雅的方式轻松解决。
这篇文章希望以实际案例为切入点,结合 Bitmap 的原理,聊一聊它在数据仓库中的应用场景和实现方式,帮助大家更好地理解和使用这一实用工具。

二、Bitmap 原理

2.1 基础构成

Bitmap 是一个连续的位数组,数组的每个位置对应一个无符号整数。例如需要存储 1、2、4、5 元素,只需要将这个数组的第 1、2、4、5 位设置为 1 其余位保持为 0 即可,即为011011

2.2 核心操作

2.2.1 交

通过按位与计算两个位图的交集,例如 bitmap1 和 bitmap2 的位图分别是 1010 和 0110,它们的交集为 0010

2.2.2 并

通过按位或计算两个位图的并集,例如 bitmap1 和 bitmap2 的位图分别是 1010 和 0110,它们的并集为 1110

2.2.3 补

通过按位取反计算某个位图的补集,例如 bitmap 的位图是 1010,它的补集位 0101

2.2.4 差

通过按位与非计算两个位图的差集,例如 bitmap1 和 bitmap2 的位图分别是 1010 和 0110,它们的差集为 1000(bitmap & ~bitmap2)

2.2.5 基数

按位计算位图中值位 1 的数量,例如 bitmap 的位图是 1010,它的基数为 2

2.3 应用场景

位图因为其独特的数据结构在大规模数据统计场景有着内存占用低、计算效率高的特点。例如存储 100 万个连续无符号正整数(uint),直接存储大约需要 4MB(100 万 x 4byte),而使用位图只需要100 万位即可,而每位只需要 1bit(8bit = 1byte)大约只需 122KB;同时其按位计算是计算机中效率最高的计算方式之一。
位图的基本操作在生产中应用也是极其广泛的,例如:在用户行为分析中

  1. 交:同时满足多个条件的用户集合,对应 sql 中的 and
  2. 并:满足任意条件的用户集合,对应 sql 中的 or
  3. 补/差:满足条件 A 不满足条件 B 的用户集合,对应 sql 中的 and not 组合
  4. 基数:交并补差操作之后统计满足条件的用户个数,对应 sql 中的 count(distinct)

Tip: 从集合的角度来看,补和差的逻辑是一样的。只不过补的逻辑是 AB 两个集合是存在子集的关系,若 AB 不满足父子集关系就是差运算。本质都是从一个集合中扣出另外一个集合中的元素

三、Bitmap 的痛点及其优化

2.3 的例子中特地强调了连续,因为从 Bitmap 基本数据结构可以看出,位图的长度只和最大的元素有关与元素个数无关。如果需要存储一个极其离散的数据集,例如[1,1000000] 位图在需要开辟 100 万个 bit 空间,但实际只存储两条数据,这时候使用位图依然需要 122KB 的空间,但直接存储只需要 2 x 4byte = 8byte 这是其痛点之一。
第二个痛点是只能存储无符号整数,生产中的数据是五花八门的连最常用的字符串都不能处理是会影响它的使用范围的。为此本章节主要是来讨论如何解决或缓解这两个痛点

3.1 RoaringBitmap

RoaringBitmap 在 bitmap 的基础上通过分块和压缩来表示稀疏或稠密数据的整数集合且每块根据稠密性选择不同的压缩方式,其官网地址如下:https://roaringbitmap.org/
RoaringBitmap 的核心是将一个 32 位的无符号整数分为高 16 位和低 16 位两部分,其中高 16 位作为索引,每个索引对应一个 container 用来存储低 16 位的数据;同时 RoaringBitmap 还会根据数据规模以及稠密程度提供三种 container 实现

  1. ArrayContainer
  2. BitmapContainer
  3. RunContainer
    下面通过源码简单的过一下 RoaringBitmap 的实现逻辑

3.1.2 索引逻辑

RoaringBitmap 的底层实现类是 RoaringArray 下面是这个类的核心属性

static final int INITIAL_CAPACITY = 4;  // keys 的初始长度char[] keys = null;  // 存储高 16 位的索引值Container[] values = null; // 存储低 16 位的数据

下面是核心的 add 方法

public void add(final int x) {  final char hb = Util.highbits(x);  // 获取高 16 位索引值final int i = highLowContainer.getIndex(hb);  // 通过索引获取容器的索引值if (i >= 0) {  // 容器已经存在highLowContainer.setContainerAtIndex(i,  highLowContainer.getContainerAtIndex(i).add(Util.lowbits(x)));  } else {  // 容器不存在final ArrayContainer newac = new ArrayContainer();  highLowContainer.insertNewKeyValueAt(-i - 1, hb, newac.add(Util.lowbits(x)));  }}

上面的代码可以看出Container 的默认实现是 ArrayContainer
同时索引的处理逻辑可以这么理解,从 0 到 32 位无符号整数的最大值(42亿左右)按 65536 将元素划分成 65536 份分别存储到 container 中

3.1.3 容器逻辑

ArrayContainer

ArrayContainer 的核心逻辑是通过一个 char 数组按序存储低 16 位的实际值,下面是两个核心参数

private static final int DEFAULT_INIT_SIZE = 4;  // 初始容量static final int DEFAULT_MAX_SIZE = 4096;// 最大容量

所以一定伴随着数组的扩容,对于 DEFAULT_MAX_SIZE 则是当数据规模达到一定程度时存储原始数据将不再有优势需要转换到 BitmapContainer 实现

public Container add(final char x) {if (cardinality == 0 || (cardinality > 0 && (x) > (content[cardinality - 1]))) {if (cardinality >= DEFAULT_MAX_SIZE) {return toBitmapContainer().add(x); // 数据量超过阈值,转换为 bitmapcontainer}if (cardinality >= this.content.length) {increaseCapacity(); // 扩容逻辑}content[cardinality++] = x; // 保存低 16 位原始数据} else {// 通过二分法查找带插入的位置int loc = Util.unsignedBinarySearch(content, 0, cardinality, x);if (loc < 0) {// Transform the ArrayContainer to a BitmapContainer// when cardinality = DEFAULT_MAX_SIZEif (cardinality >= DEFAULT_MAX_SIZE) {return toBitmapContainer().add(x);}if (cardinality >= this.content.length) {increaseCapacity();}// insertion : shift the elements > x by one position to// the right// and put x in it's appropriate placeSystem.arraycopy(content, -loc - 1, content, -loc, cardinality + loc + 1);content[-loc - 1] = x;++cardinality;}}return this;
}
BitmapContainer

当数据量超过 4096 时再存储原始数据不管是内存还是计算效率都不占优势,因此将其转换为 Bitmap 存储。需要注意的是 RoaringBitmap 的 Bitmap 相对于位图实现是极其巧妙的。
下面是核心属性以及构造方法

public static final int MAX_CAPACITY = 1 << 16; // 最大容量 65536final long[] bitmap;public BitmapContainer() {  this.cardinality = 0;  this.bitmap = new long[MAX_CAPACITY / 64];  // 数组长度 1024
}

MAX_CAPACITY 说明一个 Container 需要存储 65536 个元素(这是低 16 位决定的),但是 BitmapContainer 却使用了 1024 长度的 long 数组来存储,总内存占用固定位 1024 x 8byte = 8KB,下面是最为精彩的添加元素操作

public Container add(final char i) {  final long previous = bitmap[i >>> 6];  // 除 64 找到该值对应的数组位置并获取数据long newval = previous | (1L << i);  // 1L << i 生成只有第 i 位为 1 其余都是 0 的值并与原值做或bitmap[i >>> 6] = newval;  // 替换原值// 更新基数if (USE_BRANCHLESS) {  cardinality += (int)((previous ^ newval) >>> i);  } else if (previous != newval) {  ++cardinality;  }  return this;  
}

至于为什么采用 long 来存储同时设计这么复杂的插入逻辑,可以从下面几点思考

  1. 现代 CPU 的指令集对 64 位(long)数据的处理效率更高
  2. 使用 long 进行按位操作(如 &、|、~)时,只需一次指令即可处理 64 位,而使用 byte 需要处理 8 次
  3. 使用 long[] 时,每个元素固定存储 64 位,通过简单的位运算可以直接访问特定位
    虽然使用 long 数组在内存使用上不是最优解,但是 BitmapContainer 是在 ArrayContainer 基础上转换过来的,本身已经是一种密集场景,因此使用 long 在计算性能上会有较大的优势,这点额外的内存开销是可以接受的
RunContainer

RunContainer 是一种特殊的容器类型,被设计在用于高效的连续数据存储,使用一个有序数组,存储数值范围的起点和终点,主要的目的是在数据高度连续时,减少内存占用和提高操作效率。
例如:[1, 2, 3, 4, 5, 10, 11, 12] 可以优化为 [(1, 5), (10, 12)]
但是因为数据的离散程度 RoaringBitmap 无法直接控制,因此 BitmapContainer 向 RunContainer 的转变是手动的,在 BitmapContainer 中提供 runOptimize 方法交由使用者自己调用

public Container runOptimize() {  int numRuns = numberOfRunsLowerBound(MAXRUNS); // decent choice  int sizeAsRunContainerLowerBound = RunContainer.serializedSizeInBytes(numRuns);  if (sizeAsRunContainerLowerBound >= getArraySizeInBytes()) {  return this;  }  // else numRuns is a relatively tight bound that needs to be exact  // in some cases (or if we need to make the runContainer the right  // size)  numRuns += numberOfRunsAdjustment();  int sizeAsRunContainer = RunContainer.serializedSizeInBytes(numRuns);  if (getArraySizeInBytes() > sizeAsRunContainer) {  return new RunContainer(this, numRuns);  } else {  return this;  }
}

3.2 Roaring64Bitmap 和 Roaring64NavigableMap

RoaringBitmap 的设计是存储 32 位无符号整数,但是对于大型网站、APP 等其用户 id 规模肯定是超过 RoaringBitmap 的设计范围,因此生产中更多的是使用 Roaring64Bitmap 或 Roaring64NavigableMap 实现,从类名就可以看出来可存储 64 位无符号整数。虽然存储的数据范围不同且实现细节也略有差异,但整体思想都是将数据分为高位和低位,高位作为索引低位存储到 container 中,因此三者的区别主要是对高位索引的处理机制上
Roaring64Bitmap 和 Roaring64NavigableMap 区别如下:

  1. Roaring64Bitmap 将高 48 位作为索引并存储到 Art 中(The Adaptive Radix Tree),低 16 位存储到 Container 中,Container 的实现与 RoaringBitmap 一致
  2. Roaring64NavigableMap 将高 32 位作为索引并存储到 NavigableMap 中,具体的实现是 TreeMap 底层是红黑树,低 32 位存储到 Container 中。有自己的实现,感兴趣可以看一下 BitmapDataProvider 的实现类(也是三种)

3.3 拓展任意数据类型

不管是 Bitmap 的哪种实现都只能处理无符号整数,对于字符串的处理可以从下面两种方向去考虑拓展

3.3.1 hash

通过 hash 将字符串转换成无符号整数,但需要考虑 hash 函数的离散型可能得到的集合数据过于离散使用 bitmap 不一定是最佳实践。

3.3.2 row_number

通过 dense_rank() over() 全窗口开窗生成连续序号。这种方案可以处理任意类型的数据同时还可以处理原集合数据过于离散的情况;但是全窗口开窗在超大数据集中效率极低甚至无法完成计算,同时需要额外的 join 操作替换掉原集合数据,这种场景是否需要继续使用 bitmap 有待商榷。

四、生产中的 Bitmap

4.1 集成 Spark/Hive

生产中的 bitmap 通常是使用 UDF 集成到 Spark/Hive 中(下面统称 UDF)。推荐使用 Doris 提供的 bitmap 实现,相对来说比较权威 https://doris.apache.org/zh-CN/docs/ecosystem/hive-bitmap-udf

Tip: doris 的 hive bitmap 在 fe 下面最终生成 jar 相对来说也比较大,可以自己重新起一个专门的 udf 项目对照写一下

4.2 实战

编写一段 shell 脚本用于生成随机的 userid

#!/bin/bash# 输入参数
MAX_ROWS=$1          # 最大行数
MAX_USERID=$2        # 最大 userid
MAX_PARALLEL=$3      # 最大并行度# 校验参数
if [[ -z "$MAX_ROWS" || -z "$MAX_USERID" || -z "$MAX_PARALLEL" ]]; thenecho "Usage: $0 <max_rows> <max_userid> <max_parallel>"exit 1
fi# 临时目录
OUTPUT_DIR="./output"
MERGED_FILE="./merged_result.txt"
mkdir -p "$OUTPUT_DIR"# 计算每个子任务分配的行数
ROWS_PER_TASK=$((MAX_ROWS / MAX_PARALLEL))
REMAINING_ROWS=$((MAX_ROWS % MAX_PARALLEL)) # 分配多余的行到最后一个任务# 打印任务分配情况
echo "总行数: $MAX_ROWS"
echo "最大用户 ID: $MAX_USERID"
echo "最大并行任务数: $MAX_PARALLEL"
echo "每个任务行数: $ROWS_PER_TASK"
echo "多余行数: $REMAINING_ROWS"# 任务计数器
TASK_COMPLETED=0# 生成随机行的函数
generate_random_users() {local task_id=$1local rows=$2local output_file=$3echo "任务 $task_id: 开始生成 $rows 行数据到 $output_file..."for ((i=1; i<=rows; i++)); doecho $((RANDOM % MAX_USERID)) >> "$output_file"doneecho "任务 $task_id: 生成完成!"
}# 启动子任务
for ((i=1; i<=MAX_PARALLEL; i++)); doOUTPUT_FILE="$OUTPUT_DIR/task_${i}.txt"ROWS_TO_GENERATE=$ROWS_PER_TASK# 最后一个任务需要分配多余的行if [[ $i -eq $MAX_PARALLEL ]]; thenROWS_TO_GENERATE=$((ROWS_PER_TASK + REMAINING_ROWS))figenerate_random_users "$i" "$ROWS_TO_GENERATE" "$OUTPUT_FILE" &
done# 等待所有子任务完成
wait
TASK_COMPLETED=$MAX_PARALLEL
echo "所有任务完成!"# 合并所有文件
echo "开始合并所有任务文件..."
cat "$OUTPUT_DIR"/*.txt > "$MERGED_FILE"
echo "合并完成,输出文件: $MERGED_FILE"# 清理临时文件
rm -rf "$OUTPUT_DIR"
echo "临时文件已清理!"

生成 1 亿行范围在 0 - 1 千万的 userid

./generate_user_id.sh 100000000 10000000 10
总行数: 100000000
最大用户 ID: 10000000
最大并行任务数: 10
每个任务行数: 10000000
多余行数: 0
任务 1: 开始生成 10000000 行数据到 ./output/task_1.txt...
任务 2: 开始生成 10000000 行数据到 ./output/task_2.txt...
任务 3: 开始生成 10000000 行数据到 ./output/task_3.txt...
任务 4: 开始生成 10000000 行数据到 ./output/task_4.txt...
任务 5: 开始生成 10000000 行数据到 ./output/task_5.txt...
任务 6: 开始生成 10000000 行数据到 ./output/task_6.txt...
任务 7: 开始生成 10000000 行数据到 ./output/task_7.txt...
任务 8: 开始生成 10000000 行数据到 ./output/task_8.txt...
任务 9: 开始生成 10000000 行数据到 ./output/task_9.txt...
任务 10: 开始生成 10000000 行数据到 ./output/task_10.txt...

导入表中

use blog;  create table user_info  
(  user_id bigint  
) stored as parquet;  create table user_info_txt  
(  user_id bigint  
) stored as textfile;load data local inpath '/Users/wjun/tmp/bitmap_data/merged_result.txt' overwrite into table user_info_txt;-- 数据量扩充 5 倍
insert overwrite table user_info  
select tbl.user_id  
from user_info_txt t  lateral view explode(array(t.user_id, t.user_id, t.user_id, t.user_id, t.user_id)) tbl as user_id

导入 bitmap jar 并创建 bitmap 相关的函数

add jars hdfs://127.0.0.1:8020/user/hive/jars/hive-bitmap-udf.jar;  
create temporary function to_bitmap AS 'com.hive.bitmap.udf.ToBitmapUDAF';  
create temporary function bitmap_union AS 'com.hive.bitmap.udf.BitmapUnionUDAF';  
create temporary function bitmap_count_v1 AS 'com.hive.bitmap.udf.BitmapCountUDF';  
create temporary function bitmap_and AS 'com.hive.bitmap.udf.BitmapAndUDF';  
create temporary function bitmap_or AS 'com.hive.bitmap.udf.BitmapOrUDF';  
create temporary function bitmap_xor AS 'com.hive.bitmap.udf.BitmapXorUDF';  
create temporary function bitmap_to_array AS 'com.hive.bitmap.udf.BitmapToArrayUDF';  
create temporary function bitmap_from_array AS 'com.hive.bitmap.udf.BitmapFromArrayUDF';  
create temporary function bitmap_contains AS 'com.hive.bitmap.udf.BitmapContainsUDF';

说明:spark 3.5.1 内置了 bitmap_count 这里为了不重名使用 bitmap_count_v1
下面主要演示 bitmap 在计算 uv 的场景实战
正常写法:

select count(distinct user_id) as uv from user_info;

进阶写法:

select count(1) as uv  
from (select user_id from user_info group by user_id);

bitmap 写法:

select bitmap_count_v1(to_bitmap(user_id)) from user_info;

即使 user_info 的数据量已经达到 5 亿,但是因为缺少对应的维度(没有 group by)测试结果大概率还是 count(distinct user_id) 最快。还是需要各位在生产环境中去验证,当开发指标时发现 count(distinct user_id) 已经出不来结果并且导致 spark 频繁的 oom 时可以考虑 bitmap 写法

讨论:理论上 count(distinct user_id) 都可以优化成嵌套 group by 写法,但是当出现一个查询列表中出现多个字段的 count distinct 嵌套 group by 可能就不是很好实现了

相关文章:

Bitmap在数仓中的应用

一、背景 在数据仓库的日常工作中&#xff0c;我们经常需要面对海量数据的存储和高效查询问题。尤其是&#xff0c;当业务对性能的要求越来越高、数据量持续增长时&#xff0c;传统的处理方式往往显得笨拙而低效。而这时候&#xff0c;Bitmap&#xff08;位图&#xff09;作为…...

C++病毒(^_^|)(2)

第二期 声明&#xff1a; 仅供损害电脑&#xff0c;不得用于非法。损坏电脑&#xff0c;作者一律不负责。此作为作者原创&#xff0c;转载请经过同意。 直接上代码 #include <bits/stdc.h> #include <windows.h> using namespace std; HHOOK g_hHook;void lrud(…...

Linux 内核架构入门:从基础概念到面试指南*

1. 引言 Linux 内核是现代操作系统的核心&#xff0c;负责管理硬件资源、提供系统调用、处理进程调度等功能。对于初学者来说&#xff0c;理解 Linux 内核的架构是深入操作系统开发的第一步。本篇博文将详细介绍 Linux 内核的架构体系&#xff0c;结合硬件、子系统及软件支持的…...

leetcode-495.提莫攻击

leetcode-495.提莫攻击 文章目录 leetcode-495.提莫攻击一.题目描述二.代码提交三.解释 一.题目描述 二.代码提交 #include <vector> using namespace std;int findPoisonedDuration(vector<int>& timeSeries, int duration) {int total 0;for (int i 0; i …...

mysql 参数max_connect_errors研究

1.在server端设置max_connect_errors3&#xff0c;超过3次连接错误就block mysql> set global max_connect_errors3; Query OK, 0 rows affected (0.00 sec) mysql> show variables like max_connect_errors; --------------------------- | Variable_name | Value…...

vscode无法ssh连接远程机器解决方案

远程服务器配置问题 原因&#xff1a;远程服务器的 SSH 服务配置可能禁止了 TCP 端口转发功能&#xff0c;或者 VS Code Server 在远程服务器上崩溃。 解决办法 检查 SSH 服务配置&#xff1a;登录到远程服务器&#xff0c;打开 /etc/ssh/sshd_config 文件&#xff0c;确保以下…...

sql盲注获取数据库的表名、列名和具体数据

1.时间盲注 获取表名 sql id1 AND IF(ASCII(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schemaDATABASE() LIMIT 1),1,1))97, SLEEP(5), 0) 获取列名 sql id1 AND IF(ASCII(SUBSTRING((SELECT column_name FROM information_schema.col…...

清华大学新闻与传播学院沈阳团队出品的《DeepSeek:从入门到精通》104页PDF

前言 本机运行DeepSeek R1大模型文章如下&#xff1a; Windows电脑本地部署运行DeepSeek R1大模型&#xff08;基于Ollama和Chatbox&#xff09;【保姆级万字教程】在Windows计算机部署DeepSeek大模型&#xff0c;给在实验室无外网的同事们用&#xff08;基于Ollama和OpenWebUI…...

使用sublime_text中,TAB键无效怎么解决???

如果你也有这样的困扰&#xff0c;请你跟着我下面的步骤操作 点击首选项&#xff08;如下图所示&#xff09; 找到下面这段代码并注释掉 { “keys”:[“tab”], “args”:{“action”:“expand_abbreviation”}, “command”:“run_emmet_action”, “context”:[ { “key”:“…...

Java IO流详解

1. IO概述 IO&#xff08;Input/Output&#xff09;即输入和输出&#xff0c;指的是设备或环境之间进行数据的输入或输出。例如&#xff0c;键盘是输入设备&#xff0c;显示器是输出设备。在Java中&#xff0c;输入输出问题通过流&#xff08;Stream&#xff09;对象来解决。以…...

智慧农业-虫害及生长预测

有害生物防控系统是一个综合性的管理体系&#xff0c;旨在预防和控制对人类生活、生产甚至生存产生危害的生物。这些生物可能包括昆虫、动物、植物、微生物乃至病毒等。 一、系统构成 1、监测预警系统&#xff1a;利用智能传感器、无人机、遥感技术等手段&#xff0c;实时监测…...

ASIL D要达到多少fit

ASIL&#xff08;Automotive Safety Integrity Level&#xff0c;汽车安全完整性等级&#xff09;D是ISO 26262标准中最高等级的安全要求&#xff0c;其对应的随机硬件故障概率目标&#xff08;以FIT表示&#xff09;需满足以下要求&#xff1a; ASIL D的FIT目标 根据 ISO 262…...

与传统光伏相比 城电科技的光伏太阳花有什么优势?

相比于传统光伏&#xff0c;城电科技的光伏太阳花有以下优势&#xff1a; 一、发电效率方面 智能追踪技术&#xff1a;光伏太阳花通过内置的智能追踪系统&#xff0c;采用全球定位跟踪算法&#xff0c;能够实时调整花瓣&#xff08;即光伏板&#xff09;的角度&#xff0c;确…...

2025年SEO工具有哪些?老品牌SEO工具有哪些

随着2025年互联网的发展和企业线上营销的日益重要&#xff0c;SEO&#xff08;搜索引擎优化&#xff09;逐渐成为了提高网站曝光率和流量的重要手段。SEO的工作不仅仅是简单地通过关键词优化和内容发布就能够实现的&#xff0c;它需要依赖一系列专业的SEO工具来帮助分析、监测和…...

深入解析与解决 Oracle 报错:ORA-29275 部分多字节字符20250213

&#x1f6e0;️ 深入解析与解决 Oracle 报错&#xff1a;ORA-29275 部分多字节字符 引言 &#x1f31f; 在与 Oracle 数据库打交道的日常工作中&#xff0c;你是否遇到过 ORA-29275: partial multibyte character 这个令人头疼的错误&#xff1f;这个错误通常与字符编码、数…...

HTML应用指南:利用GET请求获取全国海底捞门店位置信息

随着新零售业态的快速发展&#xff0c;门店位置信息的获取变得越来越重要。作为餐饮服务行业的先锋&#xff0c;海底捞不仅在服务质量上持续领先&#xff0c;还积极构建广泛的门店网络&#xff0c;以支持其不断增长的用户群体。为了更好地理解和利用这些数据&#xff0c;本篇文…...

数据流图和数据字典

在面向结构的分析和设计阶段&#xff0c;**数据流图&#xff08;Data Flow Diagram, DFD&#xff09;和数据字典&#xff08;Data Dictionary&#xff09;**是两个非常重要的工具&#xff0c;它们分别从不同的角度描述系统的功能和数据结构&#xff0c;帮助开发团队更好地理解和…...

Ubuntu 22.04 LTS 安装MinerU

1. 检测是否已安装nvidia驱动 nvidia-smi 如果看到类似如下的信息&#xff0c;说明已经安装了nvidia驱动&#xff0c;可以跳过步骤2 Note CUDA Version 显示的版本号应 > 12.1&#xff0c;如显示的版本号小于12.1&#xff0c;请升级驱动 2. 安装驱动 如没有驱动&#…...

OPEN CODER : THE OPEN COOKBOOK FOR TOP -TIER CODE LARGE LANGUAGE MODELS

Abstract 大型语言模型&#xff08;LLMs&#xff09;在代码领域已经成为不可或缺的工具&#xff0c;包括代码生成、推理任务和代理系统等多个方面。虽然开放获取的代码LLMs的性能越来越接近专有模型&#xff0c;但适合严格科学研究的优质代码LLMs&#xff0c;特别是那些具有可…...

C语言中printf()函数,格式输出符

在 C 语言中&#xff0c;printf() 函数的格式输出符&#xff08;格式说明符&#xff09;用于控制输出的格式和数据类型。以下是常见的格式说明符及其用法&#xff1a; 基本格式符 打印各种类型的值 格式输出符数据类型说明%dint输出有符号十进制整数%uunsigned int输出无符号…...

EasyRTC嵌入式WebRTC视频通话SDK支持Web浏览器、Linux、ARM、Android、iOS

随着互联网技术的飞速发展&#xff0c;实时通信&#xff08;RTC&#xff09;已经成为现代应用中不可或缺的一部分。无论是视频会议、在线教育、远程医疗&#xff0c;还是社交娱乐&#xff0c;实时通信技术都在其中扮演着重要角色。 然而&#xff0c;WebRTC技术在PC和移动端的支…...

【JS球球大作战项目实战】+在线体验

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…...

PHP高效、轻量级表格数据处理库 OpenSpout ,很好用

OpenSpout 是一个高效、轻量级的 PHP 库&#xff0c;用于处理电子表格文件&#xff08;如 Excel 和 CSV&#xff09;。它支持读取和写入大型文件&#xff0c;且内存占用低。本文将详细介绍如何安装和使用 OpenSpout。 目录 安装 基本使用 高级功能 参考文档 安装 OpenSp…...

数据库第三次作业

第一题&#xff1a; 学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;S…...

Mac 下使用多版本 Node

一、导读 使用 n 实现 Mac 下 Nodejs 的多版本切换&#xff0c;需要先安装一个版本的 Node.js&#xff0c;然后使用 npm 安装 n&#xff0c;再通过 n 管理 node 的多版本切换。 二、使用 npm 全局安装 n sudo npm install -g n 三、根据需求安装指定版本的 node sudo -E n…...

【油猴脚本/Tampermonkey】DeepSeek 服务器繁忙无限重试(20250214优化)

目录 一、 引言 二、 逻辑 三、 源代码 四、 添加新脚本 五、 使用 六、 BUG 七、 优化日志 1.获取最后消息内容报错 2.对话框切换无法正常使用 一、 引言 deepseek演都不演了&#xff0c;每次第一次提问就正常&#xff0c;后面就开始繁忙了&#xff0c;有一点阴招全…...

安全测试|SSRF请求伪造

前言 SSRF漏洞是一种在未能获取服务器权限时&#xff0c;利用服务器漏洞&#xff0c;由攻击者构造请求&#xff0c;服务器端发起请求的安全漏洞&#xff0c;攻击者可以利用该漏洞诱使服务器端应用程序向攻击者选择的任意域发出HTTP请求。 很多Web应用都提供了从其他的服务器上…...

Redisson介绍和入门使用

一、什么是Redisson&#xff1f; Redisson是一个在Redis的基础上实现的Java驻内存数据网格&#xff08;In-Memory Data Grid&#xff09;。它不仅提供了一系列的分布式的Java常用对象&#xff0c;还提供了许多分布式服务&#xff0c;其中就包含了各种分布式锁的实现。 官网地址…...

TUSB422 MCU 软件用户指南

文章目录 TUSB422 MCU 软件用户指南 目录表格图表1. 介绍2. 配置2.1 通用配置2.2 USB-PD 3.0 支持2.3 VDM 支持 3. 代码 ROM/RAM 大小优化4. 通过 UART 调试4. 移植到其他微控制器 TUSB422 MCU 软件用户指南 摘要 本文档是 TUSB422 微控制器基于 Type-C 端口控制&#xff08;…...

归并排序 和 七大算法的总结图

目录 什么是递归排序&#xff1a; 图解&#xff1a; 递归方法&#xff1a; 代码实现&#xff1a; 思路分析&#xff1a; 非递归方法&#xff1a; 思路&#xff1a; 代码实现&#xff1a; 思路分析&#xff1a; 什么是递归排序&#xff1a; 先将数据分解成诺干个序列&#xff0…...

MySQL调用存储过程和存储函数

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…...

类与对象(OOP)

类(Class) 类是对象的模板或蓝图&#xff0c;用来描述对象的属性和行为。 动态与静态是同一张图像&#xff0c;最终效果也是相同 类的组成分别由&#xff1a; 属性(成员变量)&#xff1a;描述对象的状态。 方法(成员方法):描述对象的行为。 构造函数&#xff1a;用于创建对象…...

接入 SSL 认证配置:满足等保最佳实践

前言 随着信息安全形势的日益严峻&#xff0c;等保&#xff08;信息安全等级保护&#xff09;要求成为各行业信息系统必须遵守的标准。在数据库领域&#xff0c;OpenGauss作为一款高性能、安全、可靠的开源关系型数据库&#xff0c;也需要满足等保要求&#xff0c;确保数据的安…...

PyTorch Lightning多GPU分布式日志介绍

分布式日志是指在分布式系统中,多个节点(如多台机器或多个 GPU)协同工作时,对系统运行状态、错误信息、性能指标等进行记录的过程。在 多 GPU/分布式训练 环境下,多个进程会同时运行,普通的 print() 或 logging 可能会在所有 GPU 上重复输出,导致日志混乱。PyTorch Ligh…...

LCR 160. 数据流中的中位数

文章目录 1.题目[LCR 160. 数据流中的中位数](https://leetcode.cn/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/solutions/477593/zi-jie-ti-ku-jian-41-kun-nan-shu-ju-liu-zhong-de-z/)2.思路3.代码 1.题目 LCR 160. 数据流中的中位数 **中位数 **是有序整数列表中的…...

post、get、delete、put请求

一、配置axios拦截器 // src/utils/request.js import axios from axios;// 创建 axios 实例 const instance axios.create({baseURL: https://api.example.com, // 你的 API 基础 URL//timeout: 10000, // 请求超时时间 });// 请求拦截器 instance.interceptors.request.use…...

css: 针对属性left/right/top/bottom为啥设置transition动画不起作用

如题&#xff1a; 在css的position中 left/right/top/bottom 这类位置属性值如果考虑使用transition来添加动画&#xff0c;transition它会优先考虑left/top属性&#xff0c;而此时transition触发需要的是数值型属性&#xff0c;如果设置为auto则系统会默认不考虑将位置属性添加…...

计算机网络(1)基础篇

目录 1.TCP/IP 网络模型 2.键入网址--->网页显示 2.1 生成HTTP数据包 2.2 DNS服务器进行域名与IP转换 2.3 建立TCP连接 2.4 生成IP头部和MAC头部 2.5 网卡、交换机、路由器 3 Linux系统收发网络包 1.TCP/IP 网络模型 首先&#xff0c;为什么要有 TCP/IP 网络模型&a…...

机器学习所需要的数学知识【01】

总览 导数 行列式 偏导数 概理论 凸优化-梯度下降 kkt条件...

ArcGISPro AA表O_Name字段 内容 复制到BB表BB字段里

import arcpy# 设置工作空间和要处理的表路径 resource_shape_table r"AA表.shp" # 源表路径 resource_assets_table r"BB表.shp" # 目标表路径# 使用 SearchCursor 读取源表中的 O_Name 字段 with arcpy.da.SearchCursor(resource_shape_table, [O_Na…...

2023-arXiv-CoT Prompt 思维链提示提升大型语言模型的推理能力

arXiv | https://arxiv.org/abs/2201.11903 摘要&#xff1a; 我们探讨了如何生成思维链&#xff08;一系列中间推理步骤&#xff09;显著提高大型语言模型执行复杂推理的能力。在三个大型语言模型上的实验表明&#xff0c;思维链提示提高了一系列算术、常识和符号推理任务的性…...

机器学习数学基础:21.特征值与特征向量

一、引言 在现代科学与工程的众多领域中&#xff0c;线性代数扮演着举足轻重的角色。其中&#xff0c;特征值、特征向量以及相似对角化的概念和方法&#xff0c;不仅是线性代数理论体系的核心部分&#xff0c;更是解决实际问题的有力工具。无论是在物理学中描述系统的振动模式…...

逻辑分析仪的使用-以STM32C8T6控制SG90舵机为例

STM32C8T6控制SG90舵机 1.逻辑分析仪作用 逻辑分析仪在嵌入式开发中的作用非常重要&#xff0c;它是开发、调试和排错过程中的一个不可或缺的工具。具体来说&#xff0c;逻辑分析仪的作用包括以下几个方面&#xff1a; 1.信号捕获和分析&#xff1a; 逻辑分析仪能够实时捕获多个…...

QCustomplot库运用

最近需要用到这个库显示数据&#xff0c;需要在一个曲线图4个Y轴共用一个X轴&#xff0c;并且做游标&#xff0c;跟随鼠标移动&#xff0c;并且实时反馈数据到表格中。记录一下程序。 customPlot new QCustomPlot(this); customPlot->setBackground(QBrush(QColor(204,204,…...

数据中心网络监控

数据中心是全球协作的特定设备网络&#xff0c;用来在internet网络基础设施上传递、加速、展示、计算、存储数据信息。 对于任何利用IT基础设施的企业来说&#xff0c;数据中心都是运营的核心&#xff0c;它本质上为整个业务网络托管业务应用程序和存储空间。数据中心可以是任…...

定义+类比

定义:间接量表是备选问题或者是备选语句或者是语句中选择合适的语句表达其态度&#xff0c;最后选择是一个合适的语句 A给具体的表打分&#xff0c;是直接上数字&#xff0c;而不是选择合适的语句表达其态度 B给排序&#xff0c;也是通过数字来表达其态度&#xff0c;也不是选择…...

深度学习框架探秘|PyTorch:AI 开发的灵动画笔

前一篇文章我们学习了深度学习框架——TensorFlow&#xff08;深度学习框架探秘&#xff5c;TensorFlow&#xff1a;AI 世界的万能钥匙&#xff09;。在人工智能领域&#xff0c;还有一个深度学习框架——PyTorch&#xff0c;以其独特的魅力吸引着众多开发者和研究者。它就像一…...

NAT模式 vs DR模式:LVS 负载均衡技术的优劣与适用场景

1. NAT 模式&#xff08;Network Address Translation&#xff09; 工作原理 请求路径&#xff1a;客户端请求发送到虚拟 IP&#xff08;VIP&#xff09;&#xff0c;负载均衡器&#xff08;Director&#xff09;通过 DNAT&#xff08;目标地址转换&#xff09;将请求转发到真…...

八大排序——归并排序

目录 1.基本思想 2.动态图 3.分解的时候我们可以使用递归的方式进行 代码解释 1. main 方法 2. mergeSort 方法 3. merge 方法 示例运行过程 初始数组 每轮排序后的数组 代码总结 合并两个有序序列 1.基本思想 归并排序就是递归得将原始数组递归对半分隔&#xff0c…...

YOLO算法模型检测利用手机IP摄像头

1.yolo算法训练模型 yolov5训练网络数据集_yolo数据集 csdn-CSDN博客 2.模型检测 检测文件参数&#xff1a;yolov5的detect.py中基础参数分析-CSDN博客 3.进行IP摄像头检测 3.1下载IP摄像头 3.2打开与使用IP摄像头&#xff08;确保手机与电脑在同一WiFi&#xff08;局域网…...