面试官问,如何在十亿级别用户中检查用户名是否存在?

news2025/2/25 9:51:25

面试官问,如何在十亿级别用户中检查用户名是否存在?

前言

  • 不知道大家有没有留意过,在使用一些app注册的时候,提示你用户名已经被占用了,需要更换一个,这是如何实现的呢?你可能想这不是很简单吗,去数据库里查一下有没有不就行了吗,那么假如用户数量很多,达到数亿级别呢,这又该如何是好?

数据库方案

  • 第一种方案就是查数据库的方案,大家都能够想到,代码如下:

  • public class UsernameUniquenessChecker {
        private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
        private static final String DB_USER = "your_username";
        private static final String DB_PASSWORD = "your_password";
    
        public static boolean isUsernameUnique(String username) {
            try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD)) {
                String sql = "SELECT COUNT(*) FROM users WHERE username = ?";
                try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                    stmt.setString(1, username);
                    try (ResultSet rs = stmt.executeQuery()) {
                        if (rs.next()) {
                            int count = rs.getInt(1);
                            return count == 0; // If count is 0, username is unique
                        }
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return false; // In case of an error, consider the username as non-unique
        }
    
        public static void main(String[] args) {
            String desiredUsername = "new_user";
            boolean isUnique = isUsernameUnique(desiredUsername);
            if (isUnique) {
                System.out.println("Username '" + desiredUsername + "' is unique. Proceed with registration.");
            } else {
                System.out.println("Username '" + desiredUsername + "' is already in use. Choose a different one.");
            }
        }
    }
    
    
  • 这种方法会带来如下问题:

  • 性能问题,延迟高 如果数据量很大,查询速度慢。另外,数据库查询涉及应用程序服务器和数据库服务器之间的网络通信。建立连接、发送查询和接收响应所需的时间也会导致延迟。

  • 数据库负载过高。频繁执行 SELECT 查询来检查用户名唯一性,每个查询需要数据库资源,包括CPU和I/O。

  • 可扩展性差。数据库对并发连接和资源有限制。如果注册率继续增长,数据库服务器可能难以处理数量增加的传入请求。垂直扩展数据库(向单个服务器添加更多资源)可能成本高昂并且可能有限制。

缓存方案

  • 为了解决数据库调用用户名唯一性检查的性能问题,引入了高效的Redis缓存。

  • 在这里插入图片描述

  • public class UsernameCache {
    
        private static final String REDIS_HOST = "localhost";
        private static final int REDIS_PORT = 6379; 
        private static final int CACHE_EXPIRATION_SECONDS = 3600; 
    
        private static JedisPool jedisPool;
    
        // Initialize the Redis connection pool
        static {
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            jedisPool = new JedisPool(poolConfig, REDIS_HOST, REDIS_PORT);
        }
    
        // Method to check if a username is unique using the Redis cache
        public static boolean isUsernameUnique(String username) {
            try (Jedis jedis = jedisPool.getResource()) {
                // Check if the username exists in the Redis cache
                if (jedis.sismember("usernames", username)) {
                    return false; // Username is not unique
                }
            } catch (Exception e) {
                e.printStackTrace();
                // Handle exceptions or fallback to database query if Redis is unavailable
            }
            return true; // Username is unique (not found in cache)
        }
    
        // Method to add a username to the Redis cache
        public static void addToCache(String username) {
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.sadd("usernames", username); // Add the username to the cache set
                jedis.expire("usernames", CACHE_EXPIRATION_SECONDS); // Set expiration time for the cache
            } catch (Exception e) {
                e.printStackTrace();
                // Handle exceptions if Redis cache update fails
            }
        }
    
        // Cleanup and close the Redis connection pool
        public static void close() {
            jedisPool.close();
        }
    }
    
  • 这个方案最大的问题就是内存占用过大,假如每个用户名需要大约 20 字节的内存。你想要存储10亿个用户名的话,就需要20G的内存。

  • 总内存 = 每条记录的内存使用量 * 记录数 = 20 字节/记录 * 1,000,000,000 条记录 = 20,000,000,000 字节 = 20,000,000 KB = 20,000 MB = 20 GB

布隆过滤器方案

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

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

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

  • 具体的实现原理和数据结构如下图所示:

  • 在这里插入图片描述

  • 布隆过滤器的核心思想是使用一个位数组(bit array)和一组哈希函数。

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

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

  • 那么具体是怎么做的呢?

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

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

  • 本身redis支持布隆过滤器的数据结构,我们用代码简单实现了解一下:

  • import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.JedisPoolConfig;
    
    public class BloomFilterExample {
        public static void main(String[] args) {
            JedisPoolConfig poolConfig = new JedisPoolConfig();
            JedisPool jedisPool = new JedisPool(poolConfig, "localhost", 6379);
    
            try (Jedis jedis = jedisPool.getResource()) {
                // 创建一个名为 "usernameFilter" 的布隆过滤器,需要指定预计的元素数量和期望的误差率
                jedis.bfCreate("usernameFilter", 10000000, 0.01);
                
                // 将用户名添加到布隆过滤器
                jedis.bfAdd("usernameFilter", "alvin");
                
                // 检查用户名是否已经存在
                boolean exists = jedis.bfExists("usernameFilter", "alvin");
                System.out.println("Username exists: " + exists);
            }
        }
    }
    
  • 在上述示例中,我们首先创建一个名为 “usernameFilter” 的布隆过滤器,然后使用 bfAdd 将用户名添加到布隆过滤器中。最后,使用 bfExists 检查用户名是否已经存在。

  • 优点:

  • 节约内存空间,相比使用哈希表等数据结构,布隆过滤器通常需要更少的内存空间,因为它不存储实际元素,而只存储元素的哈希值。如果以 0.001 误差率存储 10 亿条记录,只需要 1.67 GB 内存,对比原来的20G,大大的减少了。

  • 高效的查找, 布隆过滤器可以在常数时间内(O(1))快速查找一个元素是否存在于集合中,无需遍历整个集合。

  • 缺点:

  • 误判率存在:布隆过滤器在判断元素是否存在时,有一定的误判率。这意味着在某些情况下,它可能会错误地报告元素存在,但不会错误地报告元素不存在。

  • 不能删除元素:布隆过滤器通常不支持从集合中删除元素,因为删除一个元素会影响其他元素的哈希值,增加了误判率。

总结

  • Redis 布隆过滤器的方案为大数据量下唯一性验证提供了一种基于内存的高效解决方案,它需要在内存消耗和错误率之间取得一个平衡点。当然布隆过滤器还有更多应用场景,比如防止缓存穿透、防止恶意访问等。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

StarRocks中有趣的点

最近在工作中,遇到有小伙伴使用StarRocks,所以看了下文档,感觉以下几点比较有趣。 支持MySQL协议 想必是为了通用性,StarRocks 提供MySQL协议接口,支持标准SQL语法。即可通过MySQL客户端连接StarRocks,并…

Linux系统SSH远程管理服务概述

目录 一.SSH协议 1.定义 2.优点 (1)加密 (2)压缩 3.SSH的客户端与服务端 (1)客户端 (2)服务端 4.原理 5.实验:使用ssh远程登录 二.OpenSSH服务器 1.概念 2.…

Wargames与bash知识17

Wargames与bash知识17 Bandit25(Bandit26) 关卡提示 从bandit25登录到bandit26应该相当容易…用户bandit26的shell不是/bin/bash,而是其他东西。找出它是什么,它是如何工作的,以及如何摆脱它。 推荐命令 ssh, cat, …

ssm基于vue的儿童教育网站的设计与实现论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理,然而,随着近些年信息技术的迅猛发展,让许多比较老套的信息管理模式进行了更新迭代,视频信息因为其管理内容繁杂,管理数量繁多导致手工进行处理不能满足广大…

Java锁的分类

系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 第七章 Spring Cloud 之 GateWay 第八章 Sprin…

关于Web Tours

Web Tours 文章目录 简介下载解压安装并启动地址注册登录特殊情况 简介 Web Tours是惠普loadrunner自带的一个飞机订票系统网站,默认支持SQL Server数据库、Access、Mysql等多种数据库,基于ie、Chrome、Firefox等浏览器,Web Tours网站主要提…

Jenkins-执行脚本案例-初步认识JenKins的使用

环境搭建 docker pull jenkins/jenkins:2.440 docker run -d -p 10240:8080 -p 10241:50000 -v /env/liyong/data/docker/jenkins_mount:/var/jenkins_home -v /etc/localtime:/etc/localtime --name jenkins jenkins/jenkins:2.440 #在挂载的目录下去修改仓库地址 vim hudson…

mysql复制表的几种常用方法

遇到需要拷贝一个表及其数据的情况,总结了一下几种方法 1.使用 show create table 旧表 将结果拷贝出来,将旧表名换成新表名即可. 注意:该方法仅适用于拷贝表结构,不需要同步数据的情况 show create table 旧表名2.create table 新表 like 旧表 该语句将完全拷贝旧表结构, …

15个为你的品牌增加曝光的维基百科推广方法-华媒舍

维基百科是全球最大的免费在线百科全书,拥有庞大的用户群体和高质量的内容。在如今竞争激烈的市场中,利用维基百科推广品牌和增加曝光度已成为许多企业的重要策略。本文将介绍15种方法,帮助你有效地利用维基百科推广品牌,提升曝光…

代码随想录算法训练营第四天|24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II,总结

系列文章目录 代码随想录算法训练营第一天|数组理论基础,704. 二分查找,27. 移除元素 代码随想录算法训练营第二天|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II 代码随想录算法训练营第三天|链表理论基础&#xff…

【IDEA】瑞_IDEA模版注释设置_IDEA自动生成注释模版(详细图文步骤)

文章目录 1 概要2 类的自定义模版注释3 自定义模版注释3.1 方法的自定义模版注释3.2 属性的自定义模版注释 🙊 前言:在Java开发中,注释具有不可或缺的重要性,注释负责解释代码,能帮助开发人员深入理解代码的逻辑和功能…

强化学习应用(三):基于Q-learning算法的无人车配送路径规划(提供Python代码)

一、Q-learning算法介绍 Q-learning是一种强化学习算法,用于解决基于环境的决策问题。它通过学习一个Q-table来指导智能体在不同状态下采取最优动作。下面是Q-learning算法的基本步骤: 1. 定义环境:确定问题的状态和动作空间,并…

宝塔面板安装MySQL8数据库

第一步:搜索mysql 第二步: 点击安装 我这里选择安装8版本 第三步:给宝塔配置mysql防火墙 第四步:修改数据库密码 第五步:想要使用navicat连接 需要修改root的权限 (1)使用secureCRT先登录mysql (2) 输入u…

Sentinel限流、熔断

1、限流 单个服务节点限流 sentinel 提供了两种不同的隔离机制:信号量隔离和线程池隔离,它们的主要区别如下: 信号量隔离(Semaphore Isolation): 原理:信号量隔离基于计数器(或称令…

电池均衡管理

一、前言 在电芯批量生产过程中,由于原料及生产工艺的波动,电芯的容量、内阻、电压及自放电率均会有一定的偏差,同时在电芯使用过程中随着充放电循环次数增加及存储时间、温度等影响,电芯容量衰减也会出现不一致,导致在…

有趣可操控的无人机足球大赛首次亮相,CES2024上的那些黑科技(二)

今年的CES2024(国际消费电子展)可谓是黑科技的盛宴,前天我们分享了5个有意思的产品,今天继续带来5个,一同探寻那些令人兴奋的黑科技,让我们的未来更加有趣和智能。 1400元巴掌大小AI硬件,首日卖…

STM32之OLED显示

一、模块介绍 1、常见的显示设备 LED、数码管、点阵、LCD屏(1602/12864)、OLED屏(消费电子) 2、OLED屏的概述 OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesenc…

4、C语言:指针与数组

数组与指针 指针与地址指针与函数参数指针与数组地址算数运算字符指针与函数指针数组以及指向指针的指针多维数组命令行参数指向函数的指针复杂声明 指针是一种保存变量地址的变量。C语言中,指针的使用非常广泛,原因之一是,指针常常是表达某个…

IaC基础设施即代码:使用Terraform 连接 alicloud阿里云

目录 一、实验 1.环境 2.alicloud阿里云创建用户 3.Linux使用Terraform 连接 alicloud 4.Windows使用Terraform 连接 alicloud 二、问题 1.Windows如何申明RAM 相关变量 2.Linux如何申明RAM 相关变量 3. Linux terraform 初始化失败 4.Linux terraform 计划与预览失败…

[ACM算法学习] 诱导排序与 SA-IS算法

学习自诱导排序与SA-IS算法 - riteme.site 为了简化一些操作,定义 # 是字典序最小的字符,其字典序小于字母集里任意字符,并且将其默认作为每个字符串的最后一个字符,即作 S[|S|] SA-IS 算法 SA-IS 算法是基于诱导排序这种思想。…