亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

java實(shí)現(xiàn)用戶(hù)簽到BitMap功能實(shí)現(xiàn)demo

 更新時(shí)間:2023年11月07日 10:33:57   作者:ChengKe_dawn  
這篇文章主要為大家介紹了java實(shí)現(xiàn)用戶(hù)簽到BitMap功能實(shí)現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

用戶(hù)簽到-BitMap功能演示

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

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

我們?nèi)绾文軌蚝?jiǎn)化一點(diǎn)呢?其實(shí)可以考慮小時(shí)候一個(gè)挺常見(jiàn)的方案,就是小時(shí)候,咱們準(zhǔn)備一張小小的卡片,你只要簽到就打上一個(gè)勾,我最后判斷你是否簽到,其實(shí)只需要到小卡片上看一看就知道了

我們可以采用類(lèi)似這樣的方案來(lái)實(shí)現(xiàn)我們的簽到需求。

我們按月來(lái)統(tǒng)計(jì)用戶(hù)簽到信息,簽到記錄為1,未簽到則記錄為0.

把每一個(gè)bit位對(duì)應(yīng)當(dāng)月的每一天,形成了映射關(guān)系。用0和1標(biāo)示業(yè)務(wù)狀態(tài),這種思路就稱(chēng)為位圖(BitMap)。這樣我們就用極小的空間,來(lái)實(shí)現(xiàn)了大量數(shù)據(jù)的表示

Redis中是利用string類(lèi)型數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)BitMap,因此最大上限是512M,轉(zhuǎn)換為bit則是 2^32個(gè)bit位。

BitMap的操作命令有:

  • SETBIT:向指定位置(offset)存入一個(gè)0或1
  • GETBIT :獲取指定位置(offset)的bit值
  • BITCOUNT :統(tǒng)計(jì)BitMap中值為1的bit位的數(shù)量
  • BITFIELD :操作(查詢(xún)、修改、自增)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)的位置

用戶(hù)簽到-實(shí)現(xiàn)簽到功能

需求:實(shí)現(xiàn)簽到接口,將當(dāng)前用戶(hù)當(dāng)天簽到信息保存到Redis中

思路:我們可以把年和月作為bitMap的key,然后保存到一個(gè)bitMap中,每次簽到就到對(duì)應(yīng)的位上把數(shù)字從0變成1,只要對(duì)應(yīng)是1,就表明說(shuō)明這一天已經(jīng)簽到了,反之則沒(méi)有簽到。

我們通過(guò)接口文檔發(fā)現(xiàn),此接口并沒(méi)有傳遞任何的參數(shù),沒(méi)有參數(shù)怎么確實(shí)是哪一天簽到呢?這個(gè)很容易,可以通過(guò)后臺(tái)代碼直接獲取即可,然后到對(duì)應(yīng)的地址上去修改bitMap。

代碼

UserController

@PostMapping("/sign")
 public Result sign(){
    return userService.sign();
 }

UserServiceImpl

@Override
public Result sign() {
    // 1.獲取當(dāng)前登錄用戶(hù)
    Long userId = UserHolder.getUser().getId();
    // 2.獲取日期
    LocalDateTime now = LocalDateTime.now();
    // 3.拼接key
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = USER_SIGN_KEY + userId + keySuffix;
    // 4.獲取今天是本月的第幾天
    int dayOfMonth = now.getDayOfMonth();
    // 5.寫(xiě)入Redis SETBIT key offset 1
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    return Result.ok();
}

用戶(hù)簽到-簽到統(tǒng)計(jì)

問(wèn)題1: 什么叫做連續(xù)簽到天數(shù)?
從最后一次簽到開(kāi)始向前統(tǒng)計(jì),直到遇到第一次未簽到為止,計(jì)算總的簽到次數(shù),就是連續(xù)簽到天數(shù)。

Java邏輯代碼:獲得當(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ù)了

問(wèn)題2: 如何得到本月到今天為止的所有簽到數(shù)據(jù)?

BITFIELD key GET u[dayOfMonth] 0

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

問(wèn)題3:如何從后向前遍歷每個(gè)bit位?

注意:bitMap返回的數(shù)據(jù)是10進(jìn)制,哪假如說(shuō)返回一個(gè)數(shù)字8,那么我哪兒知道到底哪些是0,哪些是1呢?我們只需要讓得到的10進(jìn)制數(shù)字和1做與運(yùn)算就可以了,因?yàn)?只有遇見(jiàn)1 才是1,其他數(shù)字都是0 ,我們把簽到結(jié)果和1進(jìn)行與操作,每與一次,就把簽到結(jié)果向右移動(dòng)一位,依次內(nèi)推,我們就能完成逐個(gè)遍歷的效果了。

需求:實(shí)現(xiàn)下面接口,統(tǒng)計(jì)當(dāng)前用戶(hù)截止當(dāng)前時(shí)間在本月的連續(xù)簽到天數(shù)

有用戶(hù)有時(shí)間我們就可以組織出對(duì)應(yīng)的key,此時(shí)就能找到這個(gè)用戶(hù)截止這天的所有簽到記錄,再根據(jù)這套算法,就能統(tǒng)計(jì)出來(lái)他連續(xù)簽到的次數(shù)了

代碼

UserController

@GetMapping("/sign/count")
public Result signCount(){
    return userService.signCount();
}

UserServiceImpl

@Override
public Result signCount() {
    // 1.獲取當(dāng)前登錄用戶(hù)
    Long userId = UserHolder.getUser().getId();
    // 2.獲取日期
    LocalDateTime now = LocalDateTime.now();
    // 3.拼接key
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = USER_SIGN_KEY + userId + keySuffix;
    // 4.獲取今天是本月的第幾天
    int dayOfMonth = now.getDayOfMonth();
    // 5.獲取本月截止今天為止的所有的簽到記錄,返回的是一個(gè)十進(jìn)制的數(shù)字 BITFIELD sign:5:202203 GET u14 0
    List<Long> result = stringRedisTemplate.opsForValue().bitField(
            key,
            BitFieldSubCommands.create()
                    .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
    );
    if (result == null || result.isEmpty()) {
        // 沒(méi)有任何簽到結(jié)果
        return Result.ok(0);
    }
    Long num = result.get(0);
    if (num == null || num == 0) {
        return Result.ok(0);
    }
    // 6.循環(huán)遍歷
    int count = 0;
    while (true) {
        // 6.1.讓這個(gè)數(shù)字與1做與運(yùn)算,得到數(shù)字的最后一個(gè)bit位  // 判斷這個(gè)bit位是否為0
        if ((num & 1) == 0) {
            // 如果為0,說(shuō)明未簽到,結(jié)束
            break;
        }else {
            // 如果不為0,說(shuō)明已簽到,計(jì)數(shù)器+1
            count++;
        }
        // 把數(shù)字右移一位,拋棄最后一個(gè)bit位,繼續(xù)下一個(gè)bit位
        num >>>= 1;
    }
    return Result.ok(count);
}

額外加餐-關(guān)于使用bitmap來(lái)解決緩存穿透的方案

回顧緩存穿透

發(fā)起了一個(gè)數(shù)據(jù)庫(kù)不存在的,redis里邊也不存在的數(shù)據(jù),通常你可以把他看成一個(gè)攻擊

解決方案:

  • 判斷id<0
  • 如果數(shù)據(jù)庫(kù)是空,那么就可以直接往redis里邊把這個(gè)空數(shù)據(jù)緩存起來(lái)

第一種解決方案:遇到的問(wèn)題是如果用戶(hù)訪(fǎng)問(wèn)的是id不存在的數(shù)據(jù),則此時(shí)就無(wú)法生效

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

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

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

現(xiàn)在的問(wèn)題是這個(gè)主鍵其實(shí)并沒(méi)有那么短,而是很長(zhǎng)的一個(gè) 主鍵

哪怕你單獨(dú)去提取這個(gè)主鍵,但是在11年左右,淘寶的商品總量就已經(jīng)超過(guò)10億個(gè)

所以如果采用以上方案,這個(gè)list也會(huì)很大,所以我們可以使用bitmap來(lái)減少list的存儲(chǔ)空間

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

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

以上就是java實(shí)現(xiàn)用戶(hù)簽到BitMap功能實(shí)現(xiàn)demo的詳細(xì)內(nèi)容,更多關(guān)于java 用戶(hù)簽到的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • spring?boot微服務(wù)場(chǎng)景下apollo加載過(guò)程解析

    spring?boot微服務(wù)場(chǎng)景下apollo加載過(guò)程解析

    apollo?是一個(gè)開(kāi)源的配置中心項(xiàng)目,功能很強(qiáng)大,apollo?本身的配置項(xiàng)并不復(fù)雜,但是因?yàn)榕渲玫穆窂教貏e多,非常容易搞混了,?所以本文試圖聚焦?spring-boot?的場(chǎng)景,在?spring-boot?微服務(wù)場(chǎng)景下,搞清楚?apollo-client的加載過(guò)程
    2022-02-02
  • Spring Boot 整合 Mockito提升Java單元測(cè)試的高效實(shí)踐案例

    Spring Boot 整合 Mockito提升Java單元測(cè)試的高效實(shí)踐案例

    Mockito與Spring Boot的整合為Java開(kāi)發(fā)者提供了一套完整的解決方案,使得單元測(cè)試更為精準(zhǔn)、高效,從而確保了代碼質(zhì)量、降低了維護(hù)成本,并促進(jìn)了項(xiàng)目的持續(xù)集成與交付,感興趣的朋友跟隨小編一起看看吧
    2024-04-04
  • java從list中取出對(duì)象并獲得其屬性值的方法

    java從list中取出對(duì)象并獲得其屬性值的方法

    這篇文章主要介紹了java從list中取出對(duì)象并獲得其屬性值的方法,大家參考使用
    2013-12-12
  • Java調(diào)用新浪api通過(guò)Ip查詢(xún)地區(qū)

    Java調(diào)用新浪api通過(guò)Ip查詢(xún)地區(qū)

    這篇文章主要介紹了Java調(diào)用新浪接口通過(guò)Ip查詢(xún)地區(qū),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • SpringBoot2.0實(shí)現(xiàn)多圖片上傳加回顯

    SpringBoot2.0實(shí)現(xiàn)多圖片上傳加回顯

    這兩天公司有需求讓做一個(gè)商戶(hù)注冊(cè)的后臺(tái)功能,其中需要商戶(hù)上傳多張圖片并回顯,本文就使用SpringBoot2.0實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-07-07
  • JAVA控制流程break?continue的示例代碼

    JAVA控制流程break?continue的示例代碼

    JAVA流程控制中有相關(guān)代碼可以終止整個(gè)流程的進(jìn)程,他們就是(break和continue),本文通過(guò)實(shí)例代碼介紹下JAVA控制流程break?continue的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2022-03-03
  • springboot如何獲取request請(qǐng)求的原始url與post參數(shù)

    springboot如何獲取request請(qǐng)求的原始url與post參數(shù)

    這篇文章主要介紹了springboot如何獲取request請(qǐng)求的原始url與post參數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 分布式Netty源碼分析EventLoopGroup及介紹

    分布式Netty源碼分析EventLoopGroup及介紹

    這篇文章主要介紹了分布式Netty源碼分析EventLoopGroup及介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • Java實(shí)現(xiàn)解數(shù)獨(dú)的小程序

    Java實(shí)現(xiàn)解數(shù)獨(dú)的小程序

    最近在學(xué)習(xí)Java,然后上個(gè)月迷上了九宮格數(shù)獨(dú),玩了幾天,覺(jué)得實(shí)在有趣,就想著能不能用編程來(lái)解決,于是就自己寫(xiě)了個(gè),還真解決了。下面這篇文章就給大家主要介紹了Java實(shí)現(xiàn)解數(shù)獨(dú)的小程序,需要的朋友可以參考借鑒。
    2017-01-01
  • Hibernatede 一對(duì)多映射配置方法(分享)

    Hibernatede 一對(duì)多映射配置方法(分享)

    下面小編就為大家?guī)?lái)一篇Hibernatede 一對(duì)多映射配置方法(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09

最新評(píng)論