欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot+Redis?BitMap實(shí)現(xiàn)簽到與統(tǒng)計(jì)的項(xiàng)目實(shí)踐

 更新時(shí)間:2023年09月16日 11:51:45   作者:ZNineSun  
最近項(xiàng)目里需要集成簽到和統(tǒng)計(jì)功能,連續(xù)簽到后會(huì)給用戶發(fā)放一些優(yōu)惠券和獎(jiǎng)品,以此來吸引用戶持續(xù)在該品臺(tái)進(jìn)行活躍,本文就詳細(xì)的介紹一下如何實(shí)現(xiàn),感興趣的可以了解一下

最近項(xiàng)目里需要集成簽到和統(tǒng)計(jì)功能,連續(xù)簽到后會(huì)給用戶發(fā)放一些優(yōu)惠券和獎(jiǎng)品,以此來吸引用戶持續(xù)在該品臺(tái)進(jìn)行活躍。下面我們一些來聊一聊目前主流的實(shí)現(xiàn)方案。

因?yàn)楹灥胶徒y(tǒng)計(jì)的功能涉及的數(shù)據(jù)量比較大,所以在如此大的數(shù)據(jù)下利用傳統(tǒng)的關(guān)系型數(shù)據(jù)庫進(jìn)行計(jì)算和統(tǒng)計(jì)是非常耗費(fèi)性能的,所以目前市面上主要依賴于高性能緩存RedisBitMap 功能來實(shí)現(xiàn)。

先看看利用Mysql實(shí)現(xiàn)以上功能會(huì)有哪些缺陷和短板。

1.使用Mysql實(shí)現(xiàn)簽到功能

首先我們需要一個(gè)簽到表

DROP TABLE IF EXISTS `tb_sign`;
CREATE TABLE `tb_sign` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `user_id` int(11) NOT NULL COMMENT '用戶Id',
  `year` year(4) NOT NULL COMMENT '簽到的年',
  `month` tinyint(2) NOT NULL COMMENT '簽到的月',
  `date` date NOT NULL COMMENT '簽到日期',
  `is_backup` tinyint(1) NOT NULL COMMENT '是否補(bǔ)簽',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

用戶一次簽到,就是一條記錄,假如有1000萬用戶,平均每人每年簽到次數(shù)為10次,則這張表一年的數(shù)據(jù)量為 1億條

每簽到一次需要使用(8 + 8 + 1 + 1 + 3 + 1)共22 字節(jié)的內(nèi)存,一個(gè)月則最多需要600多字節(jié)

這樣的壞處,占用內(nèi)存太大了,極大的消耗內(nèi)存空間!

我們可以根據(jù) Redis中 提供的 BitMap 位圖功能來實(shí)現(xiàn),每次簽到與未簽到用0 或1 來標(biāo)識(shí) ,一次存31個(gè)數(shù)字,只用了2字節(jié) 這樣我們就用極小的空間實(shí)現(xiàn)了簽到功能

2.Redis BitMap

2.1 BitMap 的操作指令

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

2.2 使用 BitMap 完成功能實(shí)現(xiàn)

利用SETBIT新增key 進(jìn)行存儲(chǔ)

SETBIT bm1 0 1

看不懂上面的指令?沒關(guān)系,我們可以通過help指令查看提示

help SETBIT

在這里插入圖片描述

通過這個(gè)指令可以看出Redis SETBIT 命令用于對(duì) key 所儲(chǔ)存的字符串值,設(shè)置或清除指定偏移量上的位(bit)。位的設(shè)置或清除取決于 value,可以是 0 或者是 1 。

當(dāng) key 不存在時(shí),自動(dòng)生成一個(gè)新的字符串值。字符串會(huì)進(jìn)行伸展以確保它可以將 value 保存在指定的偏移量上。當(dāng)字符串值進(jìn)行伸展時(shí),空白位置以 0 填充。 offset 參數(shù)必須大于或等于 0 ,小于 2^32 (bit 被限制在 512 MB 之內(nèi))。

提示:如果 offset 偏移量的值較大,計(jì)算機(jī)進(jìn)行內(nèi)存分配時(shí)可能會(huì)造成 Redis 服務(wù)器被阻塞。

在這里插入圖片描述

這樣的話我們就可以通過偏移量設(shè)置每一天的簽到情況:

  • 偏移量:表示天
  • val值:表示是否簽到
    • 已簽到設(shè)置為1
    • 未簽到設(shè)置0

下面我們只需要通過GETBIT命令就可以查看每一天的簽到情況

GETBIT bm1 2

表示查看bm1用戶第二天的簽到情況

在這里插入圖片描述

同樣,我們可以通過BITCOUNT可以統(tǒng)計(jì)出該用戶簽到了多少天

BITCOUNT bm1 

在這里插入圖片描述

BITFIELD

通過BITFIELD以原子方式操作(查詢、修改、自增)BitMap中bit數(shù)組中的指定位置(offset)的值

BITFIELD復(fù)雜度是O(n),其中n是訪問的計(jì)數(shù)器數(shù)。

語法:

BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]

參數(shù)說明:

  • key :需要操作的鍵名。
  • GET type offset :獲取指定位數(shù)的值
  • type表示數(shù)據(jù)類型,可以是無符號(hào)整數(shù)(u)或有符號(hào)整數(shù)(i)
  • offset表示偏移量,如果位不存在,返回0。
  • SET type offset value :設(shè)置指定位數(shù)的值,type和offset同上,value表示需要設(shè)置的值,因?yàn)榇嗣钪恢С衷O(shè)置8位或更少的位,所以value不能超過8個(gè)二進(jìn)制位。
  • INCRBY type offset increment :將指定位的值增加指定的增量,type和offset同上,increment表示需要增加的值,也必須不超過8個(gè)二進(jìn)制位。
  • OVERFLOW :做溢出的處理,默認(rèn)是WRAP(循環(huán)),其他兩個(gè)選項(xiàng)是SAT(飽和,超出范圍的值都設(shè)置成最大或最小值)和FAIL(不允許溢出,會(huì)返回一個(gè)錯(cuò)誤)。

使用方法

  • 獲取一個(gè)整數(shù)的二進(jìn)制位

舉個(gè)例子,我們有一個(gè)整數(shù)值10,它的二進(jìn)制位是00001010,我們想獲取它的第3位到第5位的值,即001。那么可以這樣使用:

127.0.0.1:6379> BITFIELD myint GET u3 3 u1 6
1) (integer) 1
2) (integer) 0

u3:表示3位無符號(hào)整數(shù)

指定myint鍵值,通過GET命令獲取它的第3位到第5位,結(jié)果返回一個(gè)二進(jìn)制數(shù)001,也就是十進(jìn)制的1。第二個(gè)返回值是0,表示其他未指定的位都是0。

  • 設(shè)置一個(gè)整數(shù)的二進(jìn)制位
    現(xiàn)在我們想把剛才的整數(shù)值10的第3位到第5位修改為101,即值為5??梢赃@樣設(shè)置:
127.0.0.1:6379> BITFIELD myint SET u3 3 5
(integer) 10

執(zhí)行成功后,該鍵值指定的整數(shù)值變?yōu)?3(二進(jìn)制為00001101)。

所以統(tǒng)計(jì)每個(gè)用戶的簽到情況和積分,可以使用 Redis BITFIELD 命令記錄每個(gè)用戶每天的簽到情況。

每個(gè)用戶一年365天,需要使用整數(shù)類型的BITFIELD信息記錄,每個(gè)用戶需要365個(gè)二進(jìn)制位來表示簽到情況(已簽到為1,未簽到為0),再需要一個(gè)整數(shù)位去表示用戶的總積分,就可以方便地統(tǒng)計(jì)用戶簽到情況并進(jìn)行排名。

當(dāng)然該命令的功能遠(yuǎn)不止于此,在某些系統(tǒng)中,需要對(duì)某些數(shù)據(jù)做計(jì)數(shù),比如對(duì)每個(gè)IP地址訪問次數(shù)的計(jì)數(shù)??梢允褂米址愋偷挠?jì)數(shù)器來達(dá)到這個(gè)目的,首先在Redis中創(chuàng)建一個(gè)字符串類型的計(jì)數(shù)器(值為0),通過INCRBY命令執(zhí)行增減操作,每次給IP地址所代表的計(jì)數(shù)器加1,最后獲取到的長(zhǎng)度即為IP地址對(duì)應(yīng)的訪問次數(shù)。如果訪問量過大,可以使用整數(shù)類型的BITFIELD存儲(chǔ)計(jì)數(shù)器,通過INCRBY命令執(zhí)行增減操作。這樣可以優(yōu)化性能并減少內(nèi)存占用。

BITPOS 查詢1 和 0 第一次出現(xiàn)的坐標(biāo)

在這里插入圖片描述

3.SpringBoot整合Redis實(shí)現(xiàn)簽到功能

3.1 思路分析

考慮到每月初需要重置連續(xù)簽到次數(shù),我們可以把 年和月 作為BitMap的key,然后保存到一個(gè)BitMap中,每次簽到就到對(duì)應(yīng)的位上把數(shù)字從0 變?yōu)?,只要是1,就代表是這一天簽到了,反之咋沒有簽到。

key的格式為:“USER_SIGN_KEY:” + userId + keySuffix

  • USER_SIGN_KEY:前綴
  • userId:用戶id
  • keySuffix:yyyyMM

Value則采用長(zhǎng)度為4個(gè)字節(jié)(32位)的位圖(最大月份只有31天)。位圖的每一位代表一天的簽到,1表示已簽,0表示未簽。

例如:USER_SIGN_KEY:122101:202309:表示ID=122101的用戶在2023年9月的簽到記錄

# 用戶9月17號(hào)簽到
SETBIT USER_SIGN_KEY:122101:202309 16 1 # 偏移量是從0開始,所以要把17減1
# 檢查9月17號(hào)是否簽到
GETBIT USER_SIGN_KEY:122101:202309 16 # 偏移量是從0開始,所以要把17減1
# 統(tǒng)計(jì)9月份的簽到次數(shù)
BITCOUNT USER_SIGN_KEY:122101:202309
# 獲取9月份前30天的簽到數(shù)據(jù),u30表示取0-29位的數(shù)據(jù)
BITFIELD USER_SIGN_KEY:122101:202309 get u30 0
# 獲取9月份首次簽到的日期
BITPOS USER_SIGN_KEY:122101:202309 1 # 返回的首次簽到的偏移量,加上1即為當(dāng)月的某一天

有了上面的理論支撐,下面我開始在項(xiàng)目里去集成,為了節(jié)約篇幅,只展示核心代碼,項(xiàng)目地址在文章末尾,完整代碼可下載之后自行觀看

首先需要我們項(xiàng)目里集成redis,可以參考:《springBoot集成redis(jedis)詳解》

3.2 簽到功能

簽到功能實(shí)現(xiàn)方式如下:

    @GetMapping("sign")
    public Object sign() {
        //1. 獲取登錄用戶
        Long userId = 122101L;
        //2.獲取簽到使用的key
        String key = RedisKeyUtils.createSignKey(userId);
        //3. 獲取今天是本月的第幾天設(shè)置偏移量
        LocalDateTime now = LocalDateTime.now();
        int offset = now.getDayOfMonth() - 1;
        System.out.println(String.format("入?yún)ⅲ簁ey:%s,offset:%s", JSON.toJSONString(key), JSON.toJSONString(offset)));
        //5. 寫入redis setbit key offset 1
        Boolean res = jedis.setbit(key, offset, true);
        System.out.println(String.format("出參:%s", JSON.toJSONString(res)));
        return String.format("%s簽到成功,簽到結(jié)果:%s", JSON.toJSONString(now.format(DateTimeFormatter.ofPattern("yyyyMM"))), JSON.toJSONString(res));
    }

測(cè)試:

在這里插入圖片描述

在這里插入圖片描述

簽到功能我們已經(jīng)實(shí)現(xiàn),只需要將對(duì)應(yīng)該月的某天設(shè)置為1則表示簽到成功

檢查用戶是否簽到

    @GetMapping("checkSign")
    public Object checkSign(@RequestParam(value = "sigDate") String sigDate) {
        //1. 獲取登錄用戶
        Long userId = 122101L;
        //2.獲取簽到使用的key
        LocalDateTime time = TimeUtils.str2LocalDateTime(TimeUtils.PATTERN.YYYYMMDD, sigDate);
        if (Objects.isNull(time)) {
            return false;
        }
        String key = RedisKeyUtils.createSignKey(time, userId);
        //3. 獲取今天是本月的第幾天設(shè)置偏移量
        int offset = time.getDayOfMonth() - 1;
        System.out.println(String.format("入?yún)ⅲ簁ey:%s,offset:%s", JSON.toJSONString(key), JSON.toJSONString(offset)));
        Boolean res = jedis.getbit(key, offset);
        System.out.println(String.format("出參:%s", JSON.toJSONString(res)));
        return res;
    }

訪問:http://127.0.0.1:8080/checkSign?sigDate=2023-09-07

在這里插入圖片描述

下面我們繼續(xù)看看簽到統(tǒng)計(jì)功能

3.3 簽到統(tǒng)計(jì)功能

Q1:什么叫連續(xù)簽到天數(shù)?

從最后一次簽到開始向前統(tǒng)計(jì),直到遇到第一次未簽到為止,計(jì)算總的簽到次數(shù),就是連續(xù)簽到天數(shù)。

在這里插入圖片描述

所以我們統(tǒng)計(jì)的方式很簡(jiǎn)單:

?獲得當(dāng)前這個(gè)月的最后一次簽到數(shù)據(jù),定義一個(gè)計(jì)數(shù)器,然后不停的向前統(tǒng)計(jì),直到獲得第一個(gè)非0的數(shù)字即可,每得到一個(gè)非0的數(shù)字計(jì)數(shù)器+1,直到遍歷完所有的數(shù)據(jù),就可以獲得當(dāng)前月的簽到總天數(shù)了?

Q2:如何得到本月到今天為止的所有簽到數(shù)據(jù)?

那我們則需要借助BITFIELD指令來進(jìn)行實(shí)現(xiàn)

BITFIELD key GET u[dayOfMonth-1] 0

假設(shè)今天是5號(hào),那么我們就可以從當(dāng)前月的第一天開始,獲得到當(dāng)前這一天的位數(shù),是5號(hào),那么就是5位,去拿這段時(shí)間的數(shù)據(jù),就能拿到所有的數(shù)據(jù)了,那么這5天里邊簽到了多少次呢?統(tǒng)計(jì)有多少個(gè)1即可。

Q3:如何從后向前遍歷每個(gè)Bit位?

值得我們注意的是:?bitMap返回的數(shù)據(jù)是10進(jìn)制,哪假如說返回一個(gè)數(shù)字8,那么我哪兒知道到底哪些是0,哪些是1呢??

這是一道很簡(jiǎn)單的位運(yùn)算算法題

我們只需要讓得到的10進(jìn)制數(shù)字和1做與運(yùn)算就可以了,因?yàn)?只有遇見1 才是1,其他數(shù)字都是0 ,我們把簽到結(jié)果和1進(jìn)行與操作,每與一次,就把簽到結(jié)果向右移動(dòng)一位,依次類推,我們就能完成逐個(gè)遍歷的效果了。

通過上面的幾個(gè)方法就可以很容易統(tǒng)計(jì)用戶簽到的情況。

下面看看代碼的具體實(shí)現(xiàn)

  • 獲取用戶連續(xù)簽到的天數(shù)

為了方便測(cè)試,我的當(dāng)前時(shí)間為:2023-09-07,所以我對(duì)1,3,5,6,7五天的簽到設(shè)置為1

在這里插入圖片描述

那么當(dāng)前緩存里的值則為:1010111,對(duì)應(yīng)的10進(jìn)制數(shù)為:87,最大連續(xù)1出現(xiàn)的個(gè)數(shù)為3,所以簽到的天數(shù)為3

在這里插入圖片描述

代碼如下:

    @GetMapping("signCount")
    public Object signCount() {
        //1. 獲取登錄用戶
        Long userId = 122101L;
        //2. 獲取日期
        LocalDateTime now = LocalDateTime.now();
        //3. 拼接key
        String key = RedisKeyUtils.createSignKey(now, userId);
        //4. 獲取今天是本月的第幾天
        int offset = now.getDayOfMonth();
        //5. 獲取本月截至今天為止的所有的簽到記錄,返回的是一個(gè)十進(jìn)制的數(shù)字 BITFIELD USER_SIGN_KEY1:5:202309 GET u5 0
        String type = String.format("u%d", offset);
        System.out.println(String.format("入?yún)ⅲ簁ey:%s,operationType:%s,type:%s", JSON.toJSONString(key), JSON.toJSONString(RedisHelper.OPERATION_TYPE.GET.name()), JSON.toJSONString(type)));
        List<Long> result = RedisHelper.bitField(key, RedisHelper.OPERATION_TYPE.GET, type, "0");
        //沒有任務(wù)簽到結(jié)果
        if (result == null || result.isEmpty()) {
            return 0;
        }
        Long num = result.get(0);
        if (num == null || num == 0) {
            return 0;
        }
        //6. 循環(huán)遍歷
        int count = 0;
        while (true) {
            //6.1 讓這個(gè)數(shù)字與1 做與運(yùn)算,得到數(shù)字的最后一個(gè)bit位 判斷這個(gè)數(shù)字是否為0
            if ((num & 1) == 0) {
                //如果為0,簽到結(jié)束
                break;
            } else {
                count++;
            }
            num >>>= 1;
        }
        System.out.println(String.format("緩存值:%s,簽到天數(shù):%d", JSON.toJSONString(result), count));
        return count;
    }

測(cè)試:

在這里插入圖片描述

3.4 獲取當(dāng)月首次簽到日期

利用Bitpos可以幫助我們輕松實(shí)現(xiàn)該功能

在這里插入圖片描述

  • bitpos USER_SIGN_KEY:122101:202309 1:表示1首次出現(xiàn)的位置
  • bitpos USER_SIGN_KEY:122101:202309 0:表示0首次出現(xiàn)的位置
    @GetMapping("getFirstSignDate")
    public Object getFirstSignDate(@RequestParam(value = "time") String time) {
        //1. 獲取登錄用戶
        Long userId = 122101L;
        //2. 獲取日期
        LocalDateTime dateTime = TimeUtils.str2LocalDateTime(TimeUtils.PATTERN.YYYYMMDD, time);
        //3. 拼接key
        String key = RedisKeyUtils.createSignKey(dateTime, userId);
        //4. 獲取首次出現(xiàn)的位置索引
        long pos = jedis.bitpos(key, true);
        //5. 轉(zhuǎn)換為日期
        String res = pos < 0 ? "無簽到記錄" : TimeUtils.localDateTime2String(TimeUtils.PATTERN.YYYYMMDD, dateTime.withDayOfMonth((int) (pos + 1)));
        return res;
    }

在這里插入圖片描述

3.5 獲取當(dāng)月的簽到情況

    @GetMapping("getSignInfo")
    public Object getSignInfo(@RequestParam(value = "time") String time) {
        //1. 獲取登錄用戶
        Long userId = 122101L;
        //2. 獲取日期
        LocalDateTime dateTime = TimeUtils.str2LocalDateTime(TimeUtils.PATTERN.YYYYMMDD, time);
        //當(dāng)月天數(shù)
        int monthOfDays = TimeUtils.getDayNumsOfMonth(dateTime);
        Map<String, Boolean> signMap = new HashMap<>(dateTime.getDayOfMonth());
        //3. 拼接key
        String key = RedisKeyUtils.createSignKey(dateTime, userId);
        //4. 設(shè)置位數(shù)
        String type = String.format("u%d", monthOfDays);
        System.out.println(String.format("入?yún)ⅲ簁ey:%s,operationType:%s,type:%s", JSON.toJSONString(key), JSON.toJSONString(RedisHelper.OPERATION_TYPE.GET.name()), JSON.toJSONString(type)));
        List<Long> list = RedisHelper.bitField(key, RedisHelper.OPERATION_TYPE.GET, type, "0");
        if (!CollectionUtils.isEmpty(list)) {
            Long num = list.get(0);
            int i = monthOfDays;
            while (i > 0) {
                String d = TimeUtils.localDateTime2String(TimeUtils.PATTERN.YYYYMMDD, dateTime.withDayOfMonth(i--));
                if ((num & 1) == 0) {
                    signMap.put(d, false);
                } else {
                    signMap.put(d, true);
                }
                num >>>= 1;
            }
        }
        //按照日期排序
        TreeMap sortedMap = new TreeMap(signMap);
        System.out.println("出參:" + JSON.toJSONString(sortedMap));
        return sortedMap;
    }

在這里插入圖片描述

至此簽到所需要的功能我們就全部實(shí)現(xiàn)了

本文的核心在于bitMap的使用,但是任何技術(shù)都需要有具體的落地,所以借著簽到的場(chǎng)景帶著大家具體使用一下。

當(dāng)然bitmap的落地場(chǎng)景遠(yuǎn)不止我上文中介紹的簽到統(tǒng)計(jì)等業(yè)務(wù)層面的方案,針對(duì)緩存穿透的場(chǎng)景,bitMap也是一個(gè)極佳的選擇。

4.利用BitMap解決緩存穿透

描述: 緩存穿透是指緩存和數(shù)據(jù)庫中都沒有的數(shù)據(jù),而用戶不斷發(fā)起請(qǐng)求,如發(fā)起為id為“-1”的數(shù)據(jù)或id為特別大不存在的數(shù)據(jù)。這時(shí)的用戶很可能是攻擊者,攻擊會(huì)導(dǎo)致數(shù)據(jù)庫壓力過大。

這種可以認(rèn)為是系統(tǒng)漏洞,一旦發(fā)生這種情況一般都來自于惡意請(qǐng)求。

常見解決方案:

  • 接口層增加校驗(yàn),如用戶鑒權(quán)校驗(yàn),id做基礎(chǔ)校驗(yàn),id<=0的直接攔截;
  • 從緩存取不到的數(shù)據(jù),在數(shù)據(jù)庫中也沒有取到,這時(shí)也可以將key-value對(duì)寫為key-null,緩存有效時(shí)間可以設(shè)置短點(diǎn),如30秒(設(shè)置太長(zhǎng)會(huì)導(dǎo)致正常情況也沒法使用)。這樣可以防止攻擊用戶反復(fù)用同一個(gè)id暴力攻擊

第一種解決方案:遇到的問題是如果用戶訪問的是id不存在的數(shù)據(jù),則此時(shí)就無法生效,如id遠(yuǎn)大于某個(gè)值,雖然不小于0,但是也無法進(jìn)行過濾。

第二種解決方案:遇到的問題是:如果是不同的id那就可以防止下次過來直擊數(shù)據(jù)

所以我們?nèi)绾谓鉀Q呢?

我們可以將數(shù)據(jù)庫的數(shù)據(jù),所對(duì)應(yīng)的id寫入到一個(gè)list集合中,當(dāng)用戶過來訪問的時(shí)候,我們直接去判斷l(xiāng)ist中是否包含當(dāng)前的要查詢的數(shù)據(jù),如果說用戶要查詢的id數(shù)據(jù)并不在list集合中,則直接返回,如果list中包含對(duì)應(yīng)查詢的id數(shù)據(jù),則說明不是一次緩存穿透數(shù)據(jù),則直接放行。

在這里插入圖片描述

現(xiàn)在的問題是這個(gè)主鍵其實(shí)并沒有那么短,有的可能是通過各種拼接生成的Id,是很長(zhǎng)的一個(gè)主鍵,所以如果采用以上方案,這個(gè)list也會(huì)很大,所以我們可以 使用bitmap來減少list的存儲(chǔ)空間

我們可以把list數(shù)據(jù)抽象成一個(gè)非常大的bitmap,我們不再使用list,而是將db中的id數(shù)據(jù)利用哈希思想,比如:

id 求余bitmap長(zhǎng)度 : index=id%bitmap.size

算出當(dāng)前這個(gè)id對(duì)應(yīng)應(yīng)該落在bitmap的哪個(gè)索引上,然后將這個(gè)值從0變成1,然后當(dāng)用戶來查詢數(shù)據(jù)時(shí),此時(shí)已經(jīng)沒有了list,讓用戶用他查詢的id去用相同的哈希算法, 算出來當(dāng)前這個(gè)id應(yīng)當(dāng)落在bitmap的哪一位,然后判斷這一位是0,還是1,如果是0則表明這一位上的數(shù)據(jù)一定不存在,采用這種方式來處理,需要重點(diǎn)考慮一個(gè)事情,就是誤差率, 所謂的誤差率就是指當(dāng)發(fā)生哈希沖突的時(shí)候,產(chǎn)生的誤差 。

在這里插入圖片描述

項(xiàng)目地址:https://gitee.com/ninesuntec/redisBitMapSign

到此這篇關(guān)于SpringBoot+Redis BitMap實(shí)現(xiàn)簽到與統(tǒng)計(jì)的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)SpringBoot+Redis BitMap簽到與統(tǒng)計(jì)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot如何配置多kafka

    springboot如何配置多kafka

    這篇文章主要介紹了springboot如何配置多kafka問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • MyBatis批量插入幾千條數(shù)據(jù)為何慎用foreach

    MyBatis批量插入幾千條數(shù)據(jù)為何慎用foreach

    這篇文章主要介紹了MyBatis批量插入幾千條數(shù)據(jù)為何慎用foreach問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • 如何使用try-with-resource機(jī)制關(guān)閉連接

    如何使用try-with-resource機(jī)制關(guān)閉連接

    這篇文章主要介紹了使用try-with-resource機(jī)制關(guān)閉連接的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java中實(shí)現(xiàn)定時(shí)任務(wù)的兩種方法舉例詳解

    Java中實(shí)現(xiàn)定時(shí)任務(wù)的兩種方法舉例詳解

    這篇文章主要給大家介紹了關(guān)于Java中實(shí)現(xiàn)定時(shí)任務(wù)的兩種方法,文中總結(jié)了各種實(shí)現(xiàn)方式的優(yōu)缺點(diǎn),并給出了推薦的使用場(chǎng)景,通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-12-12
  • java-SSH2實(shí)現(xiàn)數(shù)據(jù)庫和界面的分頁

    java-SSH2實(shí)現(xiàn)數(shù)據(jù)庫和界面的分頁

    本文主要是介紹SSH2實(shí)現(xiàn)數(shù)據(jù)庫和界面的分頁的代碼,分頁在web應(yīng)用中是經(jīng)常要做的事情,實(shí)用性比較大,有需要的朋友可以來了解一下。
    2016-10-10
  • SpringBoot DBUnit 單元測(cè)試(小結(jié))

    SpringBoot DBUnit 單元測(cè)試(小結(jié))

    這篇文章主要介紹了SpringBoot DBUnit 單元測(cè)試(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09
  • 前端如何傳遞Array、Map類型數(shù)據(jù)到Java后端

    前端如何傳遞Array、Map類型數(shù)據(jù)到Java后端

    這篇文章主要給大家介紹了關(guān)于前端如何傳遞Array、Map類型數(shù)據(jù)到Java后端的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-01-01
  • Mybatis結(jié)果生成鍵值對(duì)的實(shí)例代碼

    Mybatis結(jié)果生成鍵值對(duì)的實(shí)例代碼

    這篇文章主要介紹了Mybatis結(jié)果生成鍵值對(duì)的實(shí)例代碼,以及MyBatis返回Map鍵值對(duì)數(shù)據(jù)的實(shí)現(xiàn)方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下
    2017-02-02
  • SpringBoot同一接口多個(gè)實(shí)現(xiàn)類配置的實(shí)例詳解

    SpringBoot同一接口多個(gè)實(shí)現(xiàn)類配置的實(shí)例詳解

    這篇文章主要介紹了SpringBoot同一接口多個(gè)實(shí)現(xiàn)類配置的實(shí)例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • spring?jpa?審計(jì)功能自定義填充字段方式

    spring?jpa?審計(jì)功能自定義填充字段方式

    這篇文章主要介紹了spring?jpa審計(jì)功能自定義填充字段方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評(píng)論