不知道大家有没有留意过,在使用一些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."); } } }这种方法会带来如下问题:
1.性能问题,延迟高 。 如果数据量很大,查询速度慢。另外,数据库查询涉及应用程序服务器和数据库服务器之间的网络通信。建立连接、发送查询和接收响应所需的时间也会导致延迟。
2.数据库负载过高。频繁执行 SELECT 查询来检查用户名唯一性,每个查询需要数据库资源,包括CPU和I/O。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
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 检查用户名是否已经存在。