【面试题】如何在亿级别用户中检查用户名是否存在?

alt

前言

不知道大家有没有留意过,在使用一些app或者网站注册的时候,提示你用户名已经被占用了,比如我们熟知的《英雄联盟》有些人不知道取啥名字,干脆就叫“不知道取啥名”。

alt

但是有这样困惑的可不止他一个,于是就出现了“不知道取啥名1”...“不知道取啥名99” alt

需要更换一个,这是如何实现的呢?你可能想这不是很简单吗,去数据库里查一下有没有不就行了吗,那么假如用户数量很多,达到数亿级别呢,这又该如何是好? alt

解决思路

到底有哪些方案呢? 数据库可行吗? 有什么缺点呢?缓存呢?还有什么更好的方法吗?

alt

具体实现方案

关系型数据库

alt 遇事不决,先想到数据库,很多时候,数据库虽说不是最好的方案,但是都可以成为一种保底方案,所以在面试的时候,如果想到不到其他方案我们可以首先想到数据库(这里所的当然是关系型数据库啦),那数据库到底应该怎么实现呢,说来也很简单,将用户信息的name列设置为唯一索引,这样有两个好处,首先索引可以提升查询的效率,同时还能利用唯一索引的特性,将用户的名字自动去重,查询的时候,直接"select id(或name) from user where name =用户名", 如果能返回查询结果,则说明用户已经存在,需要重新写新的名字,同时我还要告诉你,这句SQL这样写还能避免回表查询,这样也会在一定程度上提升查询的效率。

这种方案虽然实现了功能,但是这样做会带来一个比较致命的问题,那就是查询速度比较慢,亿级别数据是很大的,这时候还考虑mysql的话,他的查询速度将会非常慢,这样用户的体验将会非常不好,有人可能会说了呀,那你可以分库分表呀,是的,可以这么做,但是就算分库分表你还是得扫描整个库表,这种做法解决不了根本问题。同时数据库对并发连接和资源有限制。如果注册率继续增长,数据库服务器可能难以处理数量增加的传入请求。比如像英雄联盟这种大型游戏,突然有什么活动,用户大批量涌入,进行注册,就会出现数据库难以处理持续增长的请求。

使用缓存

alt

试想一下,数据库能实现的话,我们的缓存可以实现吗?

对哦,redis天生有set这种类型的数据,我们可以设置一个key,比如:register_user,然后每次注册用户直接向缓存添加用户名,如果能成功则说明用户不重复,不能添加成功则说明用户已经被注册。这些操作都是在缓存中进行的,虽然查询速度会比mysql快,但是又会引入一个新的问题,那就是redis的大key问题。

这里补充一下什么是redis的大key问题: 普遍认同的规范是:value > 10kb,即认定为大 key,同时像list,set,hash 等容器类型的 redis key,元素数量 > 5000,即认定为大 key。

那大key会带来什么问题呢?

大 key 会带来以下四种影响:

  • 客户端超时阻塞:由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。

  • 引发网络阻塞:每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。

  • 阻塞工作线程:如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令。

  • 内存分布不均:集群模型在 slot 分片均匀情况下,会出现数据和查询倾斜情况,部分有大 key 的 Redis 节点占用内存多,QPS 也会比较大。

像我们这种业务场景必定是大key无疑了,虽然我们也可以设计一些算法将key拆分,分成不同的小key,但是又有一个新的问题出现了,假设我们每个用户名字占20个字节,那1亿用户将会耗费20G左右的内存,内存是比较珍稀且昂贵的资源,我们一下就耗费20g资源,能不能想个法子,节约一下成本,让老板觉得你是个人才,以后每次你提离职老板都亲自挽留你,并给你涨工资。(你还真别说,我有同事就是这么干的而且还真成功了,只能羡慕人家技术好啊) alt

布隆过滤器

直接缓存判断内存占用过大,有没有什么更好的办法呢?布隆过滤器就是很好的一个选择。

那究竟什么布隆过滤器呢?

布隆过滤器(Bloom Filter)是一种数据结构,用于快速检查一个元素是否存在于一个大型数据集中,通常用于在某些情况下快速过滤掉不可能存在的元素,以减少后续更昂贵的查询操作。 布隆过滤器的主要优点是它可以提供快速的查找和插入操作,并且在内存占用方面非常高效。 alt 结构如图所示,布隆过滤器的核心思想是使用一个位数组(bit array)和一组哈希函数。

  • 位数组(Bit Array) :布隆过滤器使用一个包含大量位的数组,通常初始化为全0。每个位可以存储两个值,通常是0或1。这些位被用来表示元素的存在或可能的存在。

  • 哈希函数(Hash Functions) :布隆过滤器使用多个哈希函数,每个哈希函数可以将输入元素映射到位数组的一个或多个位置。这些哈希函数必须是独立且具有均匀分布特性。哈希函数的个数越多,产生误判的概率就越低。

那么具体是怎么做的呢?

布隆过滤器的操作分为添加元素和查询元素两个阶段

  • 添加元素:如上图所示,当将字符串“name1”,“name2”插入布隆过滤器时,通过多个哈希函数将元素映射到位数组的多个位置,然后将这些位置的位设置为1。

  • 查询元素:当要检查一个元素是否存在于布隆过滤器中时,通过相同的哈希函数将元素映射到位数组的相应位置,然后检查这些位置的位是否都为1。如果有任何一个位为0,那么可以确定元素不存在于数据集中。但如果所有位都是1,元素可能存在于数据集中,但也可能是误判。

说了那么多他的优点在哪呢?

优点: 节约内存空间,相比使用哈希表等数据结构,布隆过滤器通常需要更少的内存空间,因为它不存储实际元素,而只存储元素的哈希值。

有同学可能要问了呀,你说更少就更少吗?怎么证明他确实省,像京东口号一样,"多快好省"!

alt

这里公司可以参考公式: m = -(n * ln(p)) / (ln(2)^2) 其中:m 是所需要的位数,n 是过滤器中元素的数量,p 是期望的误判率。

举个例子
alt

在这里给大家一个案例,现在有1亿用户,我们把误判率设为0.001在给定的条件下,其中 n 是10^8(1亿),p 是0.001(0.1%),我们可以将这些值带入公式中:m = -(10^8 * ln(0.001)) / (ln(2)^2) 运算后,我们得到的结果 m 大约为2.88*10^9位。为了将位转换为字节(1字节 = 8位),我们需要除以8:m_in_bytes = m / 8这将得到大约3.6*10^8字节,或者说约 0.36 GB 的内存需求。 相比原理的20G一下减少了19G还多,而且查询的时候也是O(1)的时间复杂度,对其他实现方案来说,这将是一场屠杀

难道只有优点吗?

alt

缺点 布隆过滤器在判断元素是否存在时,有一定的误判率。这意味着在某些情况下,它可能会错误地报告元素存在,但不会错误地报告元素不存在。不能删除元素,布隆过滤器通常不支持从集合中删除元素,因为删除一个元素会影响其他元素的哈希值,增加了误判率。

参看文献

https://web.archive.org/web/20110930114037/http://en.wikipedia.org/wiki/Bloom_filter#Probability_of_false_positives

https://blog.csdn.net/J_bean/article/details/135996254

https://juejin.cn/post/7293786247655129129

https://blog.csdn.net/weixin_62827806/article/details/136290340

alt

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/601044.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TypeScript系列之-理解TypeScript类型系统画图讲解

TypeScript的输入输出 如果我们把 Typescript 编译器看成一个黑盒的话。其输入则是使用 TypeScript 语法书写的文本或者文本集合。 输出是编译之后的 JS 文件 和 .d.ts 的声明文件 其中 JS 是将来需要运行的文件(里面是没有ts语法,有一个类型擦除的操作)&#xff0…

openGauss学习笔记-258 openGauss性能调优-使用Plan Hint进行调优-指定子查询不展开的Hint

文章目录 openGauss学习笔记-258 openGauss性能调优-使用Plan Hint进行调优-指定子查询不展开的Hint258.1 功能描述258.2 语法格式258.3 示例 openGauss学习笔记-258 openGauss性能调优-使用Plan Hint进行调优-指定子查询不展开的Hint 258.1 功能描述 数据库在对查询进行逻辑…

使用Code开发Django_模版和CSS

转到定义 和 查看定义 在使用Django或任何其他库的过程中,我们可能需要检查这些库中的代码。VS Code提供了两个方便的命令,可以直接导航到任何代码中的类和其他对象的定义: 转到定义 在Python开发环境中,我们可以轻松地对函数、类…

【LeetCode热题100】32. 最长有效括号(动态规划)

一.题目要求 给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 二.题目难度 困难 三.输入样例 示例 1: 输入:s “(()” 输出:2 解释:最长有效括号子…

物联网行业趋势——青创智通

工业物联网解决方案-工业IOT-青创智通 随着科技的不断进步和应用场景的日益扩大,物联网行业呈现出迅猛发展的势头。作为当今世界最具前瞻性和战略意义的领域之一,物联网行业的趋势和未来发展值得深入探讨。 ​一、物联网行业正逐渐实现全面普及。随着物…

泡泡写作怎么用 #经验分享#微信

泡泡写作是一款非常好用的论文写作工具,它可以帮助用户检测论文的相似度并进行降重,减少抄袭和重复引用的问题,是学生和研究人员们写作过程中的得力助手。 使用泡泡写作非常简便方便。用户只需要将待检测的论文文本复制粘贴到工具中&#xff…

Qt/C++项目 学生成绩管理系统

直观的 QT 图形界面:采用 QT 构建的用户友好界面,提供清晰的菜单选项,确保用户轻松导航和访问各项功能。 数据库驱动的数据存储:系统使用数据库技术安全高效地存储学生信息,保障数据的完整性和可靠性。 全面的基本功…

017——DS18B20驱动开发(基于I.MX6uLL)

目录 一、 模块介绍 1.1 简介 1.2 主要特点 1.3 存储器介绍 1.4 时序 1.5 命令 1.5.1 命令大全 1.5.2 命令使用 1.5.3 使用示例 1.6 原理图 二、 驱动程序 三、 应用程序 四、 测试 一、 模块介绍 1.1 简介 DS18B20 温度传感器具有线路简单、体积小的特点&…

MagicHut 工具分享

设计师必备工具 与全球一流设计师交流,分享在全世界的一流设计网站上展示作品,寻找灵感 免费拥有超过 20,000 TB 的设计师素材 免费下载行业设计工具,软件 其实是可以让你拥有魔法,免费使用,每天签到就行,…

2024/4/1—力扣—BiNode

代码实现: /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/void convertBiNode_pro(struct TreeNode *root, struct TreeNode **p) {if (root) {convertBiNode_pro(roo…

Python—循环加强

1.使用循环打印等腰三角形 # 画三角形层数为n # 第i层有空格:n-i # 第i层有*:2*i-1 n int(input("层数:"))#层数n for i in range(1, n 1):#范围在1~n# 画空格for _ in range(1, n - i 1): # _不用显示,用于表示&a…

windows下使用的的数字取证工作工具套装:forensictools

推荐一套windows下使用的的数字取证工作工具套装:forensictools 部分工具包括: ▫️exiftool,一个命令行应用程序和 Perl 库,用于读写元信息。 ▫️YARA,一款开源工具,用于对恶意软件样本进行识别和分类。…