分享好友 最新动态首页 最新动态分类 切换频道
java用户签到的几种实现方法以及性能优化
2024-12-26 21:05
一.方案设计
后端方案-基于数据库

在数据库中设计一张签到表,记录用户每次签到的日期及其他相关信息。然后通过时间范围查询得到用户的签到记

录示例表结构如下: 

 

通过唯一索引,可以确保同一用户在同一天内只能签到一次。通过下面的 SQL 即可查询用户的签到记录:

java代码:

实现简单这里不做演示

优点:原理简单,容易实现,适用于用户量较小的系统。

缺点:随着用户量和数据量增大,对数据库的压力增大,直接查询数据库性能较差。除了单接口的响应会增加,可能整个系统都会被其拖垮。

试想一下,每天1万个用户签到,1 个月就 30 万条数据,3 个月就接近百万的数据量了,占用硬盘空间大概 50 MB。存储 100 万个用户 365 天的签到记录,需要 17.52 GB 左右。

后端方案-基于缓存 Redis Set

可以利用内存缓存加速读写,常用的本地缓存是 Caffeine,分布式缓存是 Redis。由于每个用户会有多个签到记

,很适合使用 Redis 的 Set 类型存储,每个用户对应一个键,Set 内的每个元素为签到的具体日期。

Redis Key 的设计为:user:signins: {userId}

其中:

  • user是业务领域前缀
  • siqnins 是具体操作或功能
  • {userld} 表示每个用户,是动态值

如果 Redis 被多个项日公用,还可以在开头增加项目前缀区分,比如 mianshiniu:user:signins:fuserId}。

扩展知识:Redis 键设计规范

  • 明确性: 键名称应明确表示数据的含义和结构。例如,通过使用 signins 可以清楚地知道这个键与用户的签到记录有关。
  • 层次结构: 使用冒号 :分隔不同的部分,可以使键结构化,便于管理和查询
  • 唯一性: 确保键的唯一性,避免不同数据使用相同的键前缀。
  • 一致性: 在整个系统中保持键设计的一致性,使得管理和维护变得更加简单。
  • 长度: 避避免过长的键名称,以防影响性能和存储效率。.

具体示例如下,可以使用 Redis 命令行工具添加值到集合中:

 

使用命令查找集合中的值

 

该万案的

优点: Set 数据结构天然支持去重,适合存储和检索打卡记录

缺点: 上述设计显然存储了很多重复的字符串,针对海量数据场景,需要考虑内存的占用量。

 

其中,年份被重复存储。

为了减少内存占用,还可以在 key 中增加更多日期层级,比如 user:signins:{year}:{userId}。示例命令如下:

 

这样一来,不仅节约了内存,也便于管理,可以轻松查询某个用户在某个年份的签到情况。 存储 100 万个用户的 365 天 签到记录,使用 Redis 集合类型来存储每个用户的签到信息,每个用户需要大约 1880字节 的空间,总共需要大约 1.88GB 的内存空间,相比数据库节约了 10 倍左右。

有没有更节约内存的方式呢?

后端方案Redis Bitmap 位图(推荐)

Bitmap 位图,是一种使用位(bit)来表示数据的 紧凑 数据结构。每个位可以存储两个值:0 或 1,常用于表示某种

状态或标志。因为每个位仅占用1位内存,Bitmap 在大规模存储二值数据(如布尔值)时,非常高效且节约空间。

核心思想: 与其存储用户签到的具体日期,不如存储用户在今年的第 N 天是否签到。

 

使用位图类型存储,每个用户对应一个键,Bitmap 的 每一位 来表示用户在 某一天 是否打卡。

举个例子,我们签到的状态可以用0和1表示,0代表未签到,1代表到。 从后往前看

 

而 int 类型占用的空间为4个字节(byte),一个字节占8位(bit),即一个 int 占 32 位。在这种仅存储二值(0 或 1)的场景,就可以使用 Bitmap 位图来优化存储,因为一个 bit 就可以表示0和 1。把 int 优化成用 bit 存储,那么占用的空间可以优化 32 倍!假设原先占用的大小需要 32G,那么改造后仅需1G。

这里需要注意:

现代计算机体系结构通常以字节(8位)作为最小寻址单位,那么上述的 bit 是如何存储的呢?

答案就是 打包.

通过将多个 bit 打包到一个字节(或者其他更大的数据类型,如 int、long)中来实现的。每个字节(或数据类型)被视

为一个桶,里面可以存放若干个布尔值(0 或 1)。

对每一位操作时,要使用位运算进行访问

对于刷题签到记录场景,一个用户存储一年的数据仅需占用 46 字节,因为 46*8= 368,能覆盖 365 天的记录。那一百万用户也才占用 43.8 MB,相比于 Redis set 结构节约了 40 多倍存储空间!

1000w 个用户也才占用 438 MB! 

当然,我们没必要自己通过 int 等类型实现 Bitmap,JDK 自带了 BitSet类、Redis 也支持 Bitmap 高级数据结构。考虑到项目的分布式、可扩展性,采用 Redis 的 Bitmap 实现。

Redis Key 的设计为: user:signins:{年份}:{userId}

在 Java 程序中,还可以使用 Redisson 库提供的现成的 RBitSet,对应的redis也就是Bitmap 开发成本也很低.

这种方案

  • 优点: 内存占用极小,适合大规模用户和日期的场景。
  • 缺点 :需要熟悉位图操作,不够直观。

总结一下:

  • 基于性能的考虑,我们选用 Redis 中间件来存储用户的签到记录
  • 基于空间的考虑,我们选用 Bitmap 数据结构来存储用户的签到记录
二.java实现
2.1签到接口实现

1 在 config 目录下编写 Redisson 客户端配置类,会自动读取项目中的 Redis 配置,初始化客户端 Bean。

 

2 添加刷题签到记录接口

接口逻辑:判断目前用户当天是否签到

如果已签到,则忽略

如果未签到,则在 Bitmap 中设置记录

1)因为读写 Redis 使用的是相同的 key,可以将所有 Redis 的 key 单独定义成常量,放在 constant 日录下,还可以提供拼接完整 key 的方法。代码如下:

 

Userservice编写接口

 

实现类

 

controller

 
2.2查询用户是否签到实现

实现思路:

1.通过 userld 和当前年份从 Redis 中获取对应的 Bitmap

2.获取当前年份的总天数

3.循环天数拼接日期,根据日期去 Bitmap 中判断是否有签到记录,并记录到数组中

4.最后,将拼接好的、一年的签到记录返回给前端即可

1)在 UserService 中定义接口:

 

2)编写实现类

 
 

为什么使用了 LinkedHashMap 而不使用HashMap

因为LinkedHashMap保证了键值对映射的有序性,相当于直接得到了映射列表,符合前端要求的返回值格式。

而HashMap不保证元素的顺序,因为插入顺序和遍历顺序可能不同.

3) 编写Controller代码

 

4) 通过 Swagger 接口文档调用接口进行测试即可 使用JMeter进行压测(放最后测试了)

优化2.2查询接口

1 返回值优化

从示例结果我们可以看到 传输的数据较多、计算时间耗时、带宽占用多、效率低。

修改代码如下:

 

3、计算优化

上述代码中,我们使用循环来遍历所有年份,而循环是需要消耗 CPU 计算资源的。在Java 中的 Bitset 类中,可以使用 nextsetBit(int fromIndex)和 nextclearBit(int fromIndex)方法来获取从指定索引开始的下一个 已设置(即为 1)或 未设置(即为 0)的位。主要是2 个方法:

nextsetBit(int fromIndex):从 fromIndex 开始(包括 fromIndex 本身)寻找下一个被设置为1的位。如果找到了,返回该位的索引;如果没有找到,返回-1。

nextclearBit(int fromIndex):从 fromIndex 开始(包括 fromIndex 本身)寻找下一个为0的位。如果找到了返回该位的索引:如果没有找到,返回一个大的整数值。

使用 nextSetBit,可以跳过无意义的循环检查,通过位运算来获取被设置为1的位置,性能更高。

修改后的代码如下:

 
测试

优化前

使用JMeter压测1秒钟执行200次请求,吞吐量只有73左右

性能优化后

使用JMeter压测1秒钟执行200次请求,吞吐量可达200左右

 如有写的不对的地方欢迎指正交流

最新文章
方舟健客推出AI智能体解决方案,革新互联网医疗格局
近日,方舟健客在成都举办了以“数智同驱·解析流量密码”为主题的数智方案分享会。此次活动汇聚了腾讯健康和百度健康的高管,以及30余位知名药企负责人和行业专家,共同探讨“互联网+医疗”的市场发展趋势及数字化营销创新的解决方案。方
泰山石油涨0.16%,成交额7196.92万元,近5日主力净流入-4343.76万
12月16日,涨0.16%,成交额7196.92万元,换手率3.08%,总市值30.82亿元。根据AI大模型测算泰山石油后市走势。短期趋势看,连续2日被主力资金减仓。主力轻度控盘。中期趋势方面,下方累积一定获利筹码。近期该股有吸筹现象,但吸筹力度不强
怎样有效利用免费推广网址来提升你的网络曝光率
免费推广网址是许多企业和个人都在寻找的有效工具。通过这些网址,用户可以以低成本甚至零成本来提高他们网站的曝光率,吸引更多的访客。从社交媒体平台到讨论论坛,各种渠道都可以成为推广的重要途径。本文将详细介绍一些实用的免费推广网
能玩大型3d游戏,电脑配置清单?
要玩大型3D游戏,需要一台性能强劲的电脑。首先需要一颗高效的CPU,例如Intel的i7或AMD的Ryzen 7系列。其次,显卡也非常重要,建议选择NVidia或AMD的高端显卡,例如GTX 1070或RX 5700 XT。此外,内存条选择16GB以上,存储器建议使用SSD。主
黎曼猜想被Grok-3“成功证明”?玩笑。
Grok-3成功证明仅仅是玩笑,xAI的工程师Hieu-Pham真会玩儿。 (笔记模板由python脚本于2024年12月09日 12:26:42创建,本篇笔记适合任何的coder翻阅)【学习的细节是欢悦的历程】Python 官网:https://www.python.org/Free&
简单到一批的Wordpress Woocommerce建站实例图文详解!
​​​大家好,我是顽徒。一个从国内电商转战跨境多年的老电商。应一些同行小伙伴的要求,我会将多年以来用无数金钱测试出来的东西,以最快最简单的方式粉享给大家!愿跨境诸君共勉!从疫情开始到amazon大面积持续封号的原因,让很多跨境人
网站文章页图片大全/信息流广告加盟代理
这部分的工作比较复杂,其实写gpio,就是要学会看芯片手册。打印芯片手册,然后研读是学习的必要。最好英文版。CCM: Clock Controller Module (时钟控制模块) IOMUXC : IOMUX Controller,IO复用控制器 GPIO: Gene
金山词霸 2009 牛津版完整版+绿色精简版下载
金山软件推出了最新的《金山词霸2009牛津版》了!这次金山词霸与牛津合作,一次性增加6本牛津词典,这在牛津在全球的翻译软件合作伙伴中也属首次,实属不容易呢,可以说提升了金山词霸在翻译软件类中的权威和经典的形象了。这次《金山词霸2
笔记本电脑能外接显卡吗?
笔记本是可以加装外置显卡的。操作方法:自己DIY,在保证电源功率的情况下任意更换桌面显卡,利用的笔记本电脑中的Expresscard接口或者主板上空余的Mini PCI-E接口来进行数据交换。①优点:通用性比较高。成本投入低。②缺点:由于传输的带
如何解决网页中的文字不能复制粘贴的问题?
您是否碰到过有些网站网页上面的内容无法复制和粘贴到我们自己的电脑里面的情况?在网络上搜索和浏览网页信息,有篇网页文章的内容我很想要,很想要保留到自己的备忘录或记事本里面,但碰到这种不许复制网页内容的情况很多人也许就没办法了
相关文章
推荐文章
发表评论
0评