基于Redis實現-用戶簽到

基于Redis實現-用戶簽到

這個功能將使用到Redis中的BitMap來實現。

我們按照月來統計用戶簽到信息,簽到記錄為1,未簽到則記錄為0

在這里插入圖片描述

把每一個bit位對應當月的每一天,形成了映射關系。用0和1標示業務狀態,這種思路稱為位圖(BitMap)。

Redis中是利用String類型數據結構實現BitMap,因此最大上限是512M,轉化為bit則是2的32次方個bit位。相比于使用數據庫字段來存儲,內存使用大大減小。

1.BitMap相關命令

  • SETBIT:向指定位置(offset)存入一個0或1
  • GETBIT:獲取指定位置(offset)的bit值
  • BITCOUNT:統計BitMap中值為1的bit位的數量
  • BITFIELD:操作(查詢、修改、自增)BitMap中bit數組中的指定位置(offset)的值
  • BITFIELD_RO:獲取BitMap中bit數組,并以十進制形式返回
  • BITOP:將多個BitMap的結果做位運算(與、或、異或)
  • BITPOS:查找bit數組中指定范圍內第一個0或1出現的位置

語法演示:

1. SETBIT - 用戶簽到
# 用戶ID 1001 在第5天簽到(offset從0開始)
SETBIT user:sign:1001 4 1# 用戶ID 1001 在第10天簽到
SETBIT user:sign:1001 9 1
#-------------------------------------------------------------------------------------------
#2. GETBIT - 檢查某天是否簽到
# 檢查用戶ID 1001 第5天是否簽到
GETBIT user:sign:1001 4
# 返回1表示已簽到# 檢查用戶ID 1001 第6天是否簽到
GETBIT user:sign:1001 5
# 返回0表示未簽到
#-------------------------------------------------------------------------------------------
#3. BITCOUNT - 統計簽到總天數
# 統計用戶ID 1001 本月簽到總天數
BITCOUNT user:sign:1001
# 返回簽到的總天數
#-------------------------------------------------------------------------------------------
#4. BITFIELD - 批量操作簽到數據
# 獲取用戶ID 1001 前5天的簽到情況(以無符號5位整數形式返回)
BITFIELD user:sign:1001 GET u5 0# 同時設置多個簽到日
BITFIELD user:sign:1001 SET u1 15 1 SET u1 16 1
#-------------------------------------------------------------------------------------------
#5. BITFIELD_RO - 只讀方式獲取簽到數據
# 安全地獲取用戶ID 1001 前10天的簽到情況
BITFIELD_RO user:sign:1001 GET u10 0
#-------------------------------------------------------------------------------------------
#6. BITOP - 多用戶簽到情況統計
# 創建兩個用戶的簽到數據
SETBIT user:sign:1001 0 1
SETBIT user:sign:1001 1 1
SETBIT user:sign:1002 0 1# 統計哪些天數兩個用戶都簽到了(按位與操作)
BITOP AND both_sign user:sign:1001 user:sign:1002# 查看結果
GETBIT both_sign 0  # 返回1,表示第1天都簽到了
GETBIT both_sign 1  # 返回0,表示第2天不是都簽到了
#-------------------------------------------------------------------------------------------
#7. BITPOS - 查找連續簽到
# 查找用戶ID 1001 第一次簽到的位置(從第0位開始查找值為1的位)
BITPOS user:sign:1001 1# 查找用戶ID 1001 第一次未簽到的位置
BITPOS user:sign:1001 0
#-------------------------------------------------------------------------------------------
#完整簽到系統示例
# 用戶1001連續7天的簽到情況(1已簽,0未簽)
SETBIT user:sign:1001 0 1  # 第1天
SETBIT user:sign:1001 1 1  # 第2天
SETBIT user:sign:1001 2 0  # 第3天未簽
SETBIT user:sign:1001 3 1  # 第4天
SETBIT user:sign:1001 4 1  # 第5天
SETBIT user:sign:1001 5 0  # 第6天未簽
SETBIT user:sign:1001 6 1  # 第7天# 查詢簽到情況
BITCOUNT user:sign:1001  # 返回5(共簽到了5天)
BITPOS user:sign:1001 0  # 返回2(第一個未簽到的位置)

2.使用Java實現簡單的用戶簽到

注意:因為BitMap底層是基于String數據結構,因此其操作都封裝在字符串操作中。

在這里插入圖片描述

1.實現用戶簽到

//在ServiceImpl@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result sign() {//1.獲取當前用戶Long UserId = UserHolder.getUser().getId();//2.獲取當前日期LocalDateTime now = LocalDateTime.now();//3.拼接keyString KeySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM"));String key="sign:"+UserId+":"+KeySuffix;//4.獲取今天是本月的第幾天int dayOfMonth = now.getDayOfMonth();//5.寫入redisstringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);return Result.ok();}

2.Java實現統計連續簽到

1.什么是;連續簽到天數?

從最后一次簽到開始向前統計,直到遇到第一次未簽到為止,計算總的簽到次數,就是連續的簽到天數。

2.如何得到本月到今天為止的所有簽到數據?

BITFIELD key GET u[dayOfMonth] 0

3.如何從后向前遍歷每個bit位?

與1做與運算,就能得到最后一個bit位。

隨后右移一位,下一個bit為就成為了最后一個bit位。

//在ServiceImpl@Autowiredprivate StringRedisTemplate stringRedisTemplate;
@Override
public Result signCount() {// 1.獲取當前登錄用戶IDLong UserId = UserHolder.getUser().getId();// 2.獲取當前日期時間(帶時區)LocalDateTime now = LocalDateTime.now();// 3.拼接Redis鍵:sign:用戶ID:年月(例如:sign:1001:202310)String KeySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM"));String key = "sign:" + UserId + ":" + KeySuffix;// 4.獲取今天是本月的第幾天(1-31)int dayOfMonth = now.getDayOfMonth();// 5.使用BITFIELD命令獲取本月簽到數據的位圖(返回無符號整數)// 格式:BITFIELD key GET u<dayOfMonth> 0// 表示從偏移量0開始,獲取dayOfMonth長度的無符號整數List<Long> longs = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));// 5.1 處理空結果情況if (longs == null || longs.isEmpty()) {return Result.ok(0);  // 無簽到記錄返回0}// 5.2 獲取位圖轉換后的十進制數值Long num = longs.get(0);if (num == null || num == 0) {return Result.ok(0);  // 數值為0表示無簽到}// 6.通過位運算計算連續簽到天數int count = 0;while (true) {// 6.1 檢查最低位是否為1(與1做按位與運算)// 結果為0表示未簽到,1表示已簽到if ((num & 1) == 0) {break;  // 遇到未簽到日終止循環} else {count++;  // 簽到日計數器+1}// 6.2 無符號右移一位(相當于刪除已檢查的最低位)// 例如:1011(11) >>> 1 = 0101(5)num >>>= 1;}// 7.返回連續簽到天數return Result.ok(count);
}

關于BITFIELD參數使用解釋

  1. key

    • 作用:Redis 中存儲 BitMap 的鍵名

    • 示例"sign:1001:202310"(用戶1001在2023年10月的簽到數據)

    • 底層命令BITFIELD key [GET type offset]

    • 說明:指定要操作的 BitMap 鍵

  2. BitFieldSubCommands.create()

    • 作用:創建 BITFIELD 命令的子命令構建器

    • 說明

      • Spring Data Redis 的封裝方法,用于構建復雜的 BITFIELD 操作
      • 對應 Redis 原生命令中的 [GET/SET/INCR ...] 部分
  3. get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth))

  • 作用:指定要獲取的位段類型和長度

  • 參數分解

    • BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)
      • unsigned:表示獲取無符號整數(值始終 ≥0)
      • dayOfMonth:整數位數長度(例如今天是10月25日,則 dayOfMonth=25
      • 底層邏輯:Redis 會將從偏移量0開始的25個bit轉換為一個無符號整數
  • 示例
    如果簽到數據為 1011...(二進制),unsigned(25) 會將其轉換為十進制整數(如 123456)。

  1. valueAt(0)

    • 作用:指定要獲取的起始位偏移量(offset)

    • 參數

      • 0:表示從 BitMap 的第0位開始獲取
    • 關鍵點

      • 偏移量從0開始計數(與 SETBIT/GETBIT 的偏移量規則一致)
      • 配合 unsigned(dayOfMonth) 表示:從第0位開始,獲取連續 dayOfMonth 個bit
  2. valueAt(0)

    • 作用:指定要獲取的起始位偏移量(offset)

    • 參數

      • 0:表示從 BitMap 的第0位開始獲取
    • 關鍵點

      • 偏移量從0開始計數(與 SETBIT/GETBIT 的偏移量規則一致)
      • 配合 unsigned(dayOfMonth) 表示:從第0位開始,獲取連續 dayOfMonth 個bit

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/903788.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/903788.shtml
英文地址,請注明出處:http://en.pswp.cn/news/903788.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何用GPU Instancing來優化樹木草石重復模型

1&#xff09;如何用GPU Instancing來優化樹木草石重復模型 2&#xff09;Unity ASTC壓縮后的紋理在部分安卓機型上不顯示 3&#xff09;現在大部分項目的豎版UI設計分辨率是多少 4&#xff09;Android上拖拽物體不實時跟隨手指的問題 這是第430篇UWA技術知識分享的推送&#x…

Java面試高頻問題(31-33)

三十一、服務網格&#xff1a;東西向流量治理與故障注入 服務網格架構分層 mermaid graph BT subgraph Control Plane APilot --> BEnvoy Sidecar CMixer --> B DCitadel --> B end subgraph Data Plane B --> E服務A B --> F服務B B --> G服務C end 核心能…

初學python的我開始Leetcode題8-3

提示&#xff1a;100道LeetCode熱題-8-3主要是二叉樹相關&#xff0c;包括三題&#xff1a;將有序數組轉換為二叉搜索樹、驗證二叉搜索樹、二叉搜索樹中第K小的元素。由于初學&#xff0c;所以我的代碼部分僅供參考。 目錄 前言 題目1&#xff1a;將有序數組轉換為二叉搜索樹…

1996-2022年全國31省ZF干預度數據/財政干預度數據(含原始數據+計算過程+結果)

1996-2022年全國31省ZF干預度數據/財政干預度數據&#xff08;含原始數據計算過程結果&#xff09; 1、時間&#xff1a;1996-2022年 2、來源&#xff1a;國家統計局和各省年鑒 3、指標&#xff1a;地方財政一般預算支出、地區生產總值&#xff08;GDP&#xff09;、ZF干預度…

g4f升級到0.5.2.0版本了,但是有些機器無法運行,只能降級到0.5.1.2版本

g4f升級到0.5.2.0版本了&#xff0c;跟0.5.1.2更以前的版本相比&#xff0c;主要更新為增加了可以設置Huggingface等供應商的key Providers API key HuggingFace:Get API key HuggingSpace: 因為很多模型都會調用Huggingface&#xff0c;所以最好設置Huggingface的API key。…

C語言教程(二十五):C 語言函數可變參數詳解

引言: 在 C 語言編程中,有時我們需要處理參數數量不固定的情況,比如常見的 printf 函數,它可以根據格式化字符串的要求接受任意數量的參數。這種能接受不確定數量參數的函數,就是可變參數函數。下面將深入探討其定義、實現原理、使用方式、示例以及注意事項。 一、可變參…

OpenStack Yoga版安裝筆記(25)Nova Cell理解

1、Nova Cell概述 &#xff08;官方文檔&#xff1a;Cells (v2) — nova 25.2.2.dev5 documentation&#xff09; Nova中的cells功能的目的是允許較大的部署將其多個計算節點分割成多個cell。所有的nova部署都默認是cell部署&#xff0c;即使大多數情況下只有單一cell。這意味…

Java Set<String>:如何高效判斷是否包含指定字符串?

在 Java 開發中&#xff0c;我們經常使用 Set 集合來存儲一組唯一性的元素。特別是 HashSet&#xff0c;由于其基于哈希表的實現&#xff0c;在進行元素查找&#xff08;判斷是否包含&#xff09;時通常具有非常高的效率&#xff08;平均時間復雜度 O(1)&#xff09;。 那么&a…

MySQL 查找指定表名的表的主鍵

原理 SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME 表名 AND CONSTRAINT_NAME PRIMARY方法 public static String getPk(String tableName) {String sql "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TA…

Java大廠面試突擊:從Spring Boot自動配置到Kafka分區策略實戰解析

第一輪核心知識 面試官:請解釋Spring Boot中自動配置的工作原理并演示如何自定義一個@ConfigurationProperties組件? xbhog:自動配置通過EnableAutoConfiguration注解觸發,結合當前環境判斷(如是否檢測到MyBatis依賴)和條件注解(@ConditionalOnClass)來決定是否啟用配…

開發板型號 ESP32-DevKitC-32模塊型號 ESP32-WROOM-32 和主控芯片 ESP32-D0WDQ6-V3

以下是關于開發板型號 ESP32-DevKitC-32、模塊型號 ESP32-WROOM-32 和主控芯片 ESP32-D0WDQ6-V3 的詳細介紹&#xff1a; 開發板型號&#xff1a;ESP32-DevKitC-32 概述&#xff1a;ESP32-DevKitC 是樂鑫推出的一款基于 ESP32 模組的小型開發板&#xff0c;板上模組的絕大部…

數據庫系統綜合應用與深度實踐指南

前言 在當今數據驅動的時代&#xff0c;數據庫技術已成為信息系統的核心支柱。從簡單的數據存儲到復雜的企業級應用&#xff0c;數據庫系統支撐著現代社會的方方面面。本文作為一篇綜合性的數據庫科普文章&#xff0c;旨在為讀者提供從基礎到進階的完整知識體系&#xff0c;涵…

vscode 的空格和 tab 設置 與 Rime 自建詞庫

自動保存&#xff08;多用于失去焦點時保存&#xff09; Files: Auto Save 推薦不勾 保存時格式化&#xff08;Pritter 插件的功能&#xff0c;自動使用 Pritter 的格式&#xff09; Editor: Format On Save 推薦不勾 tab 的空格數量&#xff0c;2 或 4 Editor: Tab Size 推薦…

【Python爬蟲詳解】第五篇:使用正則表達式提取網頁數據

在前面幾篇文章中&#xff0c;我們介紹了幾種強大的HTML解析工具&#xff1a;BeautifulSoup、XPath和PyQuery。這些工具都是基于HTML結構來提取數據的。然而&#xff0c;有時我們需要處理的文本可能沒有良好的結構&#xff0c;或者我們只關心特定格式的字符串&#xff0c;這時正…

論文報錯3

idm不讓用&#xff1a; powershell管理員運行&#xff1a; irm https://raw.githubusercontent.com/lstprjct/IDM-Activation-Script/main/IAS.ps1 | iex 選擇1&#xff1a; 輸入9&#xff1a;

數據結構-樹(二叉樹、紅黑、B、B+等)

?樹的基本定義? 樹的定義 樹&#xff08;Tree&#xff09;?? 是一種 ??非線性數據結構??&#xff0c;由 ??節點&#xff08;Node&#xff09;?? 和 ??邊&#xff08;Edge&#xff09;?? 組成&#xff0c;滿足以下條件&#xff1a; ??有且僅有一個根節點&am…

【Android】四大組件

目錄 1. Activity 2. Service 3. BroadcastReceiver 4. ContentProvider 四大組件各自承擔著不同的職責&#xff0c;彼此之間協同工作&#xff0c;共同為用戶提供一個流暢的APP體驗。 1. Activity 負責展示用戶界面&#xff0c;就像App的一個個“頁面”&#xff0c;用戶通…

Java 多線程進階:線程安全、synchronized、死鎖、wait/notify 全解析(含代碼示例)

在 Java 并發編程中&#xff0c;“線程安全” 是核心議題之一。本文將深入講解線程安全的實現手段、synchronized 的使用方式、可重入鎖、死鎖的成因與避免、wait/notify 通信機制等&#xff0c;并配合實際代碼案例&#xff0c;幫助你徹底搞懂 Java 線程協作機制。 一、線程安全…

高并發場景下的MySQL生存指南

引言 在2025年全球數字經濟峰會上&#xff0c;阿里云披露其核心交易系統單日處理請求量突破萬億次&#xff0c;其中MySQL集群承載了78%的OLTP業務。這標志著數據庫系統已進入百萬級QPS時代&#xff0c;傳統優化手段面臨三大挑戰&#xff1a; 一、硬件與架構優化&#xff1a;構…

MCP入門

什么是mcp mcp&#xff08;model context protocol&#xff0c;模型上下文協議&#xff09; 標準化協議&#xff1a;讓大模型用統一的方式來調用工具&#xff0c;是llm和工具之間的橋梁 A2A&#xff1a;Agent-to-Agent協議 mcp通信機制 提供mcp服務查詢的平臺 具有工具合集…