redis | bitmap、雪花ID、分布式 | 踩坑日志

3868 字
19 分钟
redis | bitmap、雪花ID、分布式 | 踩坑日志

redis | bitmap、雪花ID、分布式:一场注定失败的组合#

Bitmap 是 Redis 中极其精巧的数据结构——用 1 个 bit 标记一个用户状态,1 亿用户仅需 12 MB。但当它遇上雪花 ID(64 bit)和分布式架构时,一切美好都化为泡影。本文从三个维度(Bitmap 本身、雪花 ID、分布式)深度剖析这个经典踩坑,并给出最终方案。


〇案发现场#

用户首次点击”收藏”按钮,接口响应 1.5 秒,再次点击只需 20ms。Redis 中多出了一个占用 256 KB 的 Key。

# 第一次收藏(Key 不存在)
SETBIT collect:bit:1:2051776854918803458 1823456789 1
→ 耗时: 1500ms ← 😰 用户肉眼可见的卡顿
→ Redis 内存增长: 256 KB(分配 offset 0~18 亿的 bit 空间并清零)
# 第二次收藏(Key 已存在)
SETBIT collect:bit:1:2051776854918803458 3456789012 1
→ 耗时: 20ms ← ✅ 正常速度
# 第三次收藏
SETBIT collect:bit:1:2051776854918803458 987654321 1
→ 耗时: 18ms ← ✅ 正常速度

关键线索

指标首次操作后续操作差异倍数
响应时间1500 ms20 ms75 倍
Redis 内存变化+256 KB无变化-
Key 状态不存在 → 创建已存在 → 修改-

为什么第一次这么慢?为什么后续又正常了?256 KB 从何而来?下面逐步拆解。


一、问题现象#

使用 Redis Bitmap 存储用户交互状态(点赞/收藏/关注)时,首次写入某个 Key 极慢(数秒甚至超时),后续操作正常。

典型代码:

// 收藏操作:SETBIT 设置用户位
Boolean wasCollected = redisTemplate.opsForValue().setBit(
"collect:bit:1:" + targetId,
toOffset(userId), // offset 可达 42.9 亿
true
);

其中 offset 转换逻辑:

public static long toOffset(Long userId) {
long hash = hash64(userId);
return Math.abs(hash) % (2^32 - 1); // 哈希取模,范围 0 ~ 42.9 亿
}

二、从 Bitmap 本身分析#

2.1 SETBIT 的时间复杂度陷阱#

Redis 官方文档对 SETBIT 的复杂度定义:

场景时间复杂度说明
Key 已存在,offset 在已有范围内O(1)直接修改对应 bit
Key 不存在(首次写入)O(offset)从字节 0 到目标 offset 分配并零初始化整段内存

这是问题的直接原因:Redis Bitmap 底层是一个原始 bit 数组,首次 SETBIT 时必须把 0 到 offset 之间的所有字节都分配出来并清零。

2.2 内存分配量化#

offset 经哈希取模后均匀分布在 0 ~ 42.9 亿之间,期望值约 21.4 亿。

offset 大小首次 SETBIT 分配内存耗时量级
100 万~125 KB毫秒级
1 亿~12 MB百毫秒级
10 亿~120 MB秒级
21.4 亿(期望值)~256 MB数秒
42.9 亿(最坏)~512 MB超时风险

2.3 为什么后续操作快?#

Key 一旦存在,内存已分配完毕,后续 SETBIT/GETBIT 都是 O(1),所以第二次及之后操作正常。这也解释了为什么问题容易被忽视——开发/测试时第二次操作就正常了,只有”首次”才会暴露。

2.4 能否缩小 Offset 范围?#

直觉方案:把取模上限从 2^32 缩小到 2^20(约 100 万),首次写入只需分配 128 KB。

问题:哈希碰撞导致数据错误。

不同 userId 可能映射到同一个 offset:

场景碰撞后果
用户 A 和 B 映射到同一 offsetA 收藏后,查询 B 的收藏状态返回 true(误判)
B 取消收藏把 A 的收藏状态也清掉了

碰撞概率(生日悖论公式):

Offset 上限 M单内容 100 人操作单内容 1000 人操作单内容 1 万人操作首次分配内存
2^32 (42.9 亿)≈ 0.0001%≈ 0.01%≈ 1.2%512 MB
2^28 (2.68 亿)≈ 0.002%≈ 0.19%≈ 17%32 MB
2^24 (1677 万)≈ 0.03%≈ 3%≈ 95%2 MB
2^20 (104 万)≈ 0.48%≈ 100%≈ 100%128 KB

结论:缩小 Offset 到不卡的范围,碰撞率对热门内容不可接受。性能和正确性无法兼得。


三、从雪花 ID 角度分析#

3.1 雪花 ID 的结构决定了它不适合做 Bitmap offset#

雪花 ID(64 bit)的结构:

┌──────────────────────────────────────────────────────────────────┐
│ 1 bit │ 41 bit │ 10 bit │ 12 bit │
│ 符号位 │ 时间戳(ms) │ 机器ID │ 序列号 │
│ 0 │ 1746000000000+... │ 0~1023 │ 0~4095 │
└──────────────────────────────────────────────────────────────────┘

一个典型的雪花 ID:2051776854918803458(约 2 × 10^18)

核心矛盾:Bitmap offset 的有效范围是 0 ~ 2^32 - 1(约 42.9 亿),而雪花 ID 的值域是 0 ~ 2^63 - 1(约 9.2 × 10^18),差了 20 亿倍

3.2 哈希取模是”治标不治本”的妥协#

userId → hash64() 散列 → % (2^32 - 1) → offset

这带来了两个问题:

问题 1:取模后的值仍然很大

hash64() 输出均匀分布在 Long.MIN_VALUE ~ Long.MAX_VALUE,取模后均匀分布在 0 ~ 2^32-1。期望值约 21.4 亿,首次写入仍需分配 ~256 MB。

问题 2:取模引入碰撞

取模是压缩映射,必然存在多对一关系。两个不同的 userId 可能映射到同一个 offset,导致数据错误。

3.3 雪花 ID 的”时间递增”特性也无法利用#

有人可能想:雪花 ID 是递增的,早期用户的 ID 较小,offset 不会太大?

不对——因为 hash64() 打散了原始 ID 的递增特性:

userId=1 → hash64 → 0x7A3B2C1D4E5F6A7B → % 2^32 → 1,823,456,789
userId=2 → hash64 → 0x1234567890ABCDEF → % 2^32 → 3,456,789,012
userId=100 → hash64 → 0xFEDCBA0987654321 → % 2^32 → 987,654,321

即使 userId 很小,hash 后的 offset 仍然可能很大。递增特性被哈希完全破坏了。

3.4 如果不用哈希,直接用雪花 ID 做 offset 呢?#

更糟——雪花 ID 本身就是 19 位数字(~2^61),远超 Bitmap offset 上限 2^32,直接用会报错:

ERR bit offset is not an integer or out of range

3.5 从 ID 生成策略角度的替代方案#

如果一定要用 Bitmap,需要让 ID 变小:

方案原理offset 上限首次分配内存问题
自增整数 IDDB auto_increment等于用户总数极小❌ 分库分表不适用,暴露业务信息
映射表userId → localId 映射等于用户总数极小⚠️ 额外维护映射关系,多一次查询
号段模式(Leaf)美团 Leaf segment等于用户总数极小⚠️ 需要引入 Leaf 组件
压缩雪花 ID只取低 32 bit2^32 ≈ 42.9 亿最大 512 MB❌ 低 32 bit 碰撞率高,回到原点

结论:在雪花 ID 体系下,没有好的办法让 ID 变小到适合 Bitmap。换数据结构比换 ID 方案成本更低。

3.6 雪花 ID 角度的本质#

雪花 ID 的本质:全局唯一、趋势递增、64 bit
Bitmap offset 的本质:紧凑整数、0~2^32、bit 位映射
两者设计目标根本不同:
雪花 ID → 保证唯一性 → 值域必须大
Bitmap offset → 紧凑映射 → 值域必须小
强行用哈希取模桥接,既丢失了唯一性(碰撞),又没解决紧凑性(offset 仍大)。

四、从分布式角度分析#

4.1 Redis 单线程阻塞:一个慢操作拖垮整个节点#

Redis 是单线程模型(命令串行执行),一个 SETBIT key 2147483648 1 需要分配 256 MB 并清零,在此期间该 Redis 节点无法响应任何其他请求

时间线:
t0 客户端A: SETBIT collect:bit:1:xxx 2147483648 1 ← 开始分配 256MB
t1 客户端B: GET user:info:123 ← 排队等待...
t2 客户端C: INCR like:count:1:456 ← 排队等待...
t3 客户端D: SADD follow:user:789:1 999 ← 排队等待...
t4 SETBIT 完成(耗时 2~5 秒) ← 客户端 B/C/D 全部超时
t5 客户端 B/C/D 的命令才开始执行

影响范围:不仅是收藏操作本身慢,同一 Redis 节点上的所有业务(登录、查询、计数)都会被阻塞。

4.2 Redis Cluster 数据倾斜#

Redis Cluster 按 Key 的 hash slot 分配到不同节点。Bitmap 方案下:

collect:bit:1:10086 → slot A → 节点 1
collect:bit:1:10087 → slot B → 节点 2
collect:bit:2:10086 → slot C → 节点 3

如果某篇爆款文章首次被大量用户收藏,所有 SETBIT 操作都打到同一个 Key,即同一个节点。该节点瞬间承受:

  • 内存分配压力(单次 256 MB)
  • CPU 压力(memset 清零)
  • 网络压力(响应延迟导致连接堆积)

而其他节点完全空闲——典型的数据倾斜

4.3 KEYS 命令的集群隐患#

同步任务中通常使用 KEYS collect:bit:* 扫描所有 Bitmap Key:

Collection<String> keys = redisTemplate.keys("collect:bit:*");

在 Redis Cluster 中:

  • KEYS 命令只扫描当前节点,需要用 SCAN 逐节点遍历
  • KEYS 是 O(N) 操作,会阻塞当前节点
  • 大量 Bitmap Key 时,同步任务本身也可能成为性能瓶颈

4.4 Bitmap 大 Key 的运维风险#

运维操作影响
DEL collect:bit:1:xxx释放 256 MB 内存,可能触发 Redis 内存碎片整理,短暂卡顿
RDB 持久化大 Key 导致 RDB 生成时间变长,fork() 耗时增加
AOF 重写大 Key 导致 AOF 重写时间变长
主从同步全量同步时大 Key 传输耗时,从节点长时间处于”加载中”状态
maxmemory单个 Key 占用数百 MB,可能触发淘汰策略误杀其他 Key

4.5 分布式角度的结论#

问题Bitmap 方案Set 方案
单线程阻塞❌ SETBIT 分配数百 MB 阻塞整个节点✅ SADD O(1),毫秒级
数据倾斜❌ 爆款内容所有操作打同一个节点✅ 用户维度 Key 天然分散到不同 slot
大 Key 风险❌ 单 Key 数百 MB✅ 单用户 Key 通常几 KB
同步扫描❌ KEYS 阻塞 + Cluster 不友好✅ 可用 pending Hash 队列,无需扫描
运维友好度❌ DEL/RDB/AOF 全受影响✅ 小 Key 无特殊影响

五、RoaringBitmap 能解决吗?#

5.1 原理#

RoaringBitmap 是压缩位图,将 32 位整数空间分成 65536 个桶,每桶按数据稀疏程度选择容器:

容器类型触发条件内存占用
Array Container桶内元素 ≤ 40962 bytes × 元素数
Bitmap Container桶内元素 > 40968 KB(固定)
Run Container连续值多更压缩

设置 offset=20 亿时,只需在对应桶里加一个元素(Array Container,2 bytes),不会分配 250 MB

5.2 三种使用方式对比#

方式 A:RedisBloom 模块(RB.* 命令)#

RB.SETBIT key 2000000000 1 -- O(1),不预分配
RB.GETBIT key 2000000000 -- O(1)
维度评价
首次写入✅ O(1),无预分配
原子性✅ Redis 单线程保证
致命问题❌ 需要安装 RedisBloom 模块,云 Redis 不一定支持

方式 B:客户端序列化(Read-Modify-Write)#

byte[] data = redisTemplate.opsForValue().get(key);
RoaringBitmap bitmap = data != null
? RoaringBitmap.deserialize(data) : new RoaringBitmap();
bitmap.add(userId);
redisTemplate.opsForValue().set(key, bitmap.serialize());
维度评价
首次写入✅ 无预分配
致命问题 1❌ 非原子操作:并发 Read-Modify-Write 导致数据丢失
致命问题 2❌ 必须加分布式锁,延迟 5~15ms
致命问题 3❌ 每次操作全量序列化/反序列化,数据量大时很慢

方式 C:Lua 脚本#

不可行——Redis 内置 Lua 不支持 RoaringBitmap 库。

5.3 RoaringBitmap 结论#

方案首次写入原子性内存效率额外依赖推荐度
RedisBloom 模块RedisBloom⭐⭐⭐⭐ 有条件推荐
客户端序列化❌ 需加锁RoaringBitmap⭐⭐ 不推荐
Set(用户维度)⚠️ 稍高⭐⭐⭐⭐⭐ 最推荐

除非能装 RedisBloom 模块,否则 RoaringBitmap 客户端方案会丢失原子性,得不偿失。


六、正确方案:String + Set(用户维度)#

6.1 Key 设计#

# 1. 操作总数(内容维度)—— String
like:count:{targetType}:{targetId} → INCR / DECR
# 2. 用户操作集合(用户维度)—— Set
like:user:{userId}:{targetType} → SADD / SREM / SISMEMBER

6.2 为什么用用户维度而不是内容维度的 Set?#

方案Key问题
like:article:{targetId} → Set<userId>内容维度爆款文章百万点赞,单 Key 几百 MB
like:user:{userId}:{targetType} → Set<targetId>用户维度单用户操作量可控,通常几百~几千

用户维度的 Set 天然上限合理,加 TTL(如 7 天)冷数据自动淘汰。

6.3 操作流程#

// 点赞
Boolean added = redisTemplate.opsForSet().isMember(
"like:user:" + userId + ":1", String.valueOf(targetId));
if (Boolean.TRUE.equals(added)) {
throw new BusinessException("已点赞");
}
redisTemplate.opsForSet().add("like:user:" + userId + ":1",
String.valueOf(targetId));
redisTemplate.opsForValue().increment("like:count:1:" + targetId);
// 异步写 MySQL
// 取消点赞
redisTemplate.opsForSet().remove("like:user:" + userId + ":1",
String.valueOf(targetId));
redisTemplate.opsForValue().decrement("like:count:1:" + targetId);
// 异步删 MySQL
// 判断是否已点赞
Boolean isLiked = redisTemplate.opsForSet().isMember(
"like:user:" + userId + ":1", String.valueOf(targetId));

6.4 缓存冷启动(Redis 无数据时)#

Boolean isLiked = redisTemplate.opsForSet().isMember(key, String.valueOf(targetId));
if (isLiked == null) {
// key 不存在,从 MySQL 回捞
RLock lock = redisson.getLock("lock:like:load:" + userId);
lock.lock();
try {
List<Long> likedIds = likeMapper.selectByUserId(userId, targetType);
if (CollUtil.isNotEmpty(likedIds)) {
redisTemplate.opsForSet().add(key,
likedIds.stream().map(String::valueOf).toArray());
}
redisTemplate.expire(key, 7, TimeUnit.DAYS);
} finally {
lock.unlock();
}
}

6.5 方案对比#

维度Bitmap + Hash(旧方案)String + Set(新方案)
首次写入性能❌ O(offset),最大 512 MB✅ O(1),始终毫秒级
判断是否已操作GETBIT O(1)SISMEMBER O(1)
数据正确性⚠️ 哈希碰撞风险✅ 零碰撞(存储原始 ID)
内存效率(1 亿用户)✅ ~12 MB⚠️ ~2 GB(但单用户维度可控)
内存效率(1 万用户)✅ ~1.2 KB✅ ~80 KB(可接受)
计数查询HGET O(1)INCR/GET O(1)
分布式友好度❌ 大 Key 阻塞单节点✅ 小 Key 天然分散
原子性✅ SETBIT 返回旧值✅ SADD/SREM 原子

6.6 前端注意事项:雪花 ID 精度丢失#

雪花 ID 约 19 位数字,超过 JS Number.MAX_SAFE_INTEGER(2^53 ≈ 16 位),JSON 序列化会丢精度:

// 前端接收后
12345678901234567891234567890123456800 // 末尾精度丢失

解决方案:Jackson 序列化时将 Long 转 String。

// 方式一:字段级注解
@JsonSerialize(using = ToStringSerializer.class)
private Long targetId;
// 方式二:全局配置 ObjectMapper
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Long.class, ToStringSerializer.instance);
module.addSerializer(Long.TYPE, ToStringSerializer.instance);
mapper.registerModule(module);
return mapper;
}

七、总结#

三维问题全景#

┌─────────────────────────────────────────────────────────────────────┐
│ 问题全景图 │
├──────────────┬──────────────────────────────────────────────────────┤
│ │ 首次写入 O(offset) 分配数百 MB 内存 │
│ Bitmap 本身 │ 缩小 Offset → 碰撞率不可接受 │
│ │ 性能与正确性无法兼得 │
├──────────────┼──────────────────────────────────────────────────────┤
│ │ 64 bit vs 32 bit offset,差 20 亿倍 │
│ 雪花 ID │ 哈希取模:既丢唯一性(碰撞),又没解决紧凑性(offset 仍大) │
│ │ 递增特性被哈希破坏,无法利用 │
│ │ 换 ID 方案成本远高于换数据结构 │
├──────────────┼──────────────────────────────────────────────────────┤
│ │ 单线程阻塞:一个慢操作拖垮整个节点 │
│ 分布式 │ 数据倾斜:爆款内容所有操作打同一个节点 │
│ │ 大 Key 运维:DEL/RDB/AOF/主从同步全受影响 │
│ │ KEYS 扫描:Cluster 不友好 + O(N) 阻塞 │
└──────────────┴──────────────────────────────────────────────────────┘

问题-方案对照表#

问题原因方案
首次写入卡顿雪花 ID → 大 offset → SETBIT O(offset) 分配内存改用 Set 结构
缩小 Offset 不可行哈希碰撞导致数据错误Set 存原始 ID,零碰撞
爆款内容 Set 过大内容维度 Set 百万成员改用用户维度 Set
单线程阻塞SETBIT 分配数百 MB 阻塞整个 Redis 节点SADD O(1) 不阻塞
数据倾斜爆款内容所有操作打同一节点用户维度 Key 天然分散
大 Key 运维风险DEL/RDB/AOF/主从同步全受影响小 Key 无特殊影响
RoaringBitmap 不可行客户端序列化丢失原子性除非有 RedisBloom 模块
雪花 ID 不兼容 Bitmap64 bit vs 32 bit offset,差 20 亿倍承认不兼容,换数据结构
前端 ID 精度丢失雪花 ID > JS MAX_SAFE_INTEGERLong 序列化为 String

最终选型#

String(计数)+ Set(用户维度,判断是否已操作)+ 异步写库。

不用 Bitmap(雪花 ID 太大),不用内容维度 Set(爆款 Key 太大),不用 RoaringBitmap(原子性难保证)。

什么时候 Bitmap 仍然适用?#

Bitmap 并非一无是处,以下场景仍然是最优选择:

场景条件示例
用户在线状态userId 为自增小整数SETBIT online 42 1
签到打卡日期作为 offset(1~31)SETBIT sign:userId:202601 15 1
布隆过滤器概率型数据结构,允许误判去重、防缓存穿透
活跃用户统计自增 ID + 按天 BitmapBITCOUNT + BITOP 统计 DAU

核心判断标准:offset 是否为紧凑小整数(万级以内)。如果是雪花 ID,直接放弃 Bitmap。

redis | bitmap、雪花ID、分布式 | 踩坑日志
https://tblog.mmzhiku.xyz/posts/projects/projects-redis-bitmap-snowflake-id/
作者
MmzMing
发布于
2026-05-06
许可协议
CC BY-NC-SA 4.0

评论区

看板娘
公告
友链 互换友链

正在招募技术类博客友链,要求原创、稳定更新。点击了解更多。

查看详情
维护 服务器升级

本周日凌晨 2:00-4:00 进行服务器维护,期间站点可能短暂无法访问。

欢迎 关于我的介绍

欢迎来到我的博客,我是深耕java、python和react技术开发。热爱技术、持续学习,欢迎同好交流探讨,也欢迎大佬互换友链。

查看详情
音乐
封面

音乐

暂未播放

0:00
0:00
暂无歌词
标签
# AI 6 # 认证 5 # 安全 4 # 登录 3 # Skill 2 # Redis 2 # Bitmap 2 # 部署 2 # Java 2 # 并发编程 2 # 性能优化 2 # 前端 1 # 博客 1 # Prompt 1 # 工作流 1 # RAG 1 # Cloudflare 1 # 缓存设计 1 # 高性能 1 # Bot 1 # Umami 1 # Vercel 1 # 线程池 1 # 虚拟线程 1 # 分布式 1 # JWT 1 # OAuth2 1 # MinIO 1 # 文件存储 1 # 扫码登录 1 # WebSocket 1 # Agent 1 # Oracle 1 # 数据库 1
目录

隐私政策

更新日期: 2026/5/19
生效日期: 2026/5/19

导言#

MmzMing的知识库 是一款由 MmzMing(以下简称“我们”)提供的产品。您在使用我们的服务时,我们可能会收集和使用您的相关信息。我们希望通过本《隐私政策》向您说明,在使用我们的服务时,我们如何收集、使用、储存和分享这些信息,以及我们为您提供的访问、更新、控制和保护这些信息的方式。

本《隐私政策》与您所使用的 MmzMing的知识库 服务息息相关,希望您仔细阅读,在需要时,按照本《隐私政策》的指引,作出您认为适当的选择。本《隐私政策》中涉及的相关技术词汇,我们尽量以简明扼要的表述,并提供进一步说明的链接,以便您的理解。

您使用或继续使用我们的服务,即意味着同意我们按照本《隐私政策》收集、使用、储存和分享您的相关信息。

如对本《隐私政策》或相关事宜有任何问题,请通过 784774835@qq.com 与我们联系。

1. 我们收集的信息#

我们或我们的第三方合作伙伴提供服务时,可能会收集、储存和使用下列与您有关的信息。如果您不提供相关信息,可能无法注册成为我们的用户或无法享受我们提供的某些服务,或者无法达到相关服务拟达到的效果。

  • 个人信息:您在注册账户或使用我们的服务时,向我们提供的相关个人信息,例如电话号码、电子邮件等。
  • 日志信息:指您使用我们的服务时,系统可能通过 cookies、标识符及相关技术收集的信息,包括您的 设备信息浏览信息点击信息,并将该等信息储存为日志信息,为您提供个性化的用户体验、保障服务安全。您可以通过浏览器设置拒绝或管理 cookie、标识符或相关技术的使用。
  • 位置信息:指您开启设备定位功能并使用我们基于位置提供的相关服务时,收集的有关您位置的信息,包括:
    • 您通过具有定位功能的移动设备使用我们的服务时,通过 GPS 或 WiFi 等方式收集的您的地理位置信息;
    • 您可以通过关闭定位功能,停止对您的地理位置信息的收集。

2. 信息的存储#

2.1 信息存储的方式和期限#

我们会通过安全的方式存储您的信息,包括本地存储(例如利用 APP 进行数据缓存)、数据库和服务器日志。

一般情况下,我们只会在为实现服务目的所必需的时间内或法律法规规定的条件下存储您的个人信息。

2.2 信息存储的地域#

我们会按照法律法规规定,将境内收集的用户个人信息存储于中国境内。

目前我们不会跨境传输或存储您的个人信息。将来如需跨境传输或存储的,我们会向您告知信息出境的目的、接收方、安全保证措施和安全风险,并征得您的同意。

2.3 产品或服务停止运营时的通知#

当我们的产品或服务发生停止运营的情况时,我们将以推送通知、公告等形式通知您,并在合理期限内删除您的个人信息或进行匿名化处理,法律法规另有规定的除外。

3. 信息安全#

我们使用各种安全技术和程序,以防信息的丢失、不当使用、未经授权阅览或披露。例如,在某些服务中,我们将利用加密技术(例如 SSL)来保护您提供的个人信息。但请您理解,由于技术的限制以及可能存在的各种恶意手段,在互联网行业,即便竭尽所能加强安全措施,也不可能始终保证信息百分之百的安全。您需要了解,您接入我们的服务所用的系统和通讯网络,有可能因我们可控范围外的因素而出现问题。

4. 我们如何使用信息#

我们可能将在向您提供服务的过程之中所收集的信息用作下列用途:

  • 向您提供服务;
  • 在我们提供服务时,用于身份验证、客户服务、安全防范、诈骗监测、存档和备份用途,确保我们向您提供的产品和服务的安全性;
  • 帮助我们设计新服务,改善我们现有服务;
  • 使我们更加了解您如何接入和使用我们的服务,从而针对性地回应您的个性化需求,例如语言设定、位置设定、个性化的帮助服务和指示,或对您和其他用户作出其他方面的回应;
  • 向您提供与您更加相关的广告以替代普遍投放的广告;
  • 评估我们服务中的广告和其他促销及推广活动的效果,并加以改善;
  • 软件认证或管理软件升级;
  • 让您参与有关我们产品和服务的调查。

5. 信息共享#

目前,我们不会主动共享或转让您的个人信息至第三方,如存在其他共享或转让您的个人信息或您需要我们将您的个人信息共享或转让至第三方情形时,我们会直接或确认第三方征得您对上述行为的明示同意。

为了投放广告,评估、优化广告投放效果等目的,我们需要向广告主及其代理商等第三方合作伙伴共享您的部分数据,要求其严格遵守我们关于数据隐私保护的措施与要求,包括但不限于根据数据保护协议、承诺书及相关数据处理政策进行处理,避免识别出个人身份,保障隐私安全。

我们不会向合作伙伴分享可用于识别您个人身份的信息(例如您的姓名或电子邮件地址),除非您明确授权。

我们不会对外公开披露所收集的个人信息,如必须公开披露时,我们会向您告知此次公开披露的目的、披露信息的类型及可能涉及的敏感信息,并征得您的明示同意。

随着我们业务的持续发展,我们有可能进行合并、收购、资产转让等交易,我们将告知您相关情形,按照法律法规及不低于本《隐私政策》所要求的标准继续保护或要求新的控制者继续保护您的个人信息。

另外,根据相关法律法规及国家标准,以下情形中,我们可能会共享、转让、公开披露个人信息无需事先征得您的授权同意:

  • 与国家安全、国防安全直接相关的;
  • 与公共安全、公共卫生、重大公共利益直接相关的;
  • 犯罪侦查、起诉、审判和判决执行等直接相关的;
  • 出于维护个人信息主体或其他个人的生命、财产等重大合法权益但又很难得到本人同意的;
  • 个人信息主体自行向社会公众公开个人信息的;
  • 从合法公开披露的信息中收集个人信息的,如合法的新闻报道、政府信息公开等渠道。

6. 您的权利#

在您使用我们的服务期间,我们可能会视产品具体情况为您提供相应的操作设置,以便您可以查询、删除、更正或撤回您的相关个人信息,您可参考相应的具体指引进行操作。此外,我们还设置了投诉举报渠道,您的意见将会得到及时的处理。如果您无法通过上述途径和方式行使您的个人信息主体权利,您可以通过本《隐私政策》中提供的联系方式提出您的请求,我们会按照法律法规的规定予以反馈。

当您决定不再使用我们的产品或服务时,可以申请注销账户。注销账户后,除法律法规另有规定外,我们将删除或匿名化处理您的个人信息。

7. 变更#

我们可能适时修订本《隐私政策》的条款。当变更发生时,我们会在版本更新时向您提示新的《隐私政策》,并向您说明生效日期。请您仔细阅读变更后的《隐私政策》内容,若您继续使用我们的服务,即表示您同意我们按照更新后的《隐私政策》处理您的个人信息。

8. 未成年人保护#

我们鼓励父母或监护人指导未满十八岁的未成年人使用我们的服务。我们建议未成年人鼓励他们的父母或监护人阅读本《隐私政策》,并建议未成年人在提交的个人信息之前寻求父母或监护人的同意和指导。