最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

用戶簽到-BitMap功能演示

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

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

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

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

我們按月來統(tǒng)計用戶簽到信息,簽到記錄為1,未簽到則記錄為0.

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

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

BitMap的操作命令有:

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

用戶簽到-實現(xiàn)簽到功能

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

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

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

代碼

UserController

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

UserServiceImpl

@Override
public Result sign() {
    // 1.獲取當(dāng)前登錄用戶
    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.寫入Redis SETBIT key offset 1
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    return Result.ok();
}

用戶簽到-簽到統(tǒng)計

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

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

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

BITFIELD key GET u[dayOfMonth] 0

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

問題3:如何從后向前遍歷每個bit位?

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

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

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

代碼

UserController

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

UserServiceImpl

@Override
public Result signCount() {
    // 1.獲取當(dāng)前登錄用戶
    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.獲取本月截止今天為止的所有的簽到記錄,返回的是一個十進(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()) {
        // 沒有任何簽到結(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.讓這個數(shù)字與1做與運算,得到數(shù)字的最后一個bit位  // 判斷這個bit位是否為0
        if ((num & 1) == 0) {
            // 如果為0,說明未簽到,結(jié)束
            break;
        }else {
            // 如果不為0,說明已簽到,計數(shù)器+1
            count++;
        }
        // 把數(shù)字右移一位,拋棄最后一個bit位,繼續(xù)下一個bit位
        num >>>= 1;
    }
    return Result.ok(count);
}

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

回顧緩存穿透

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

解決方案:

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

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

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

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

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

現(xiàn)在的問題是這個主鍵其實并沒有那么短,而是很長的一個 主鍵

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

所以如果采用以上方案,這個list也會很大,所以我們可以使用bitmap來減少list的存儲空間

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

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

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

相關(guān)文章

  • SpringBoot項目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫的過程

    SpringBoot項目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫的過程

    SQLite是一個緊湊的庫,啟用所有功能后,庫大小可以小于 750KiB, 具體取決于目標(biāo)平臺和編譯器優(yōu)化設(shè)置, 內(nèi)存使用量和速度之間需要權(quán)衡,這篇文章主要介紹了SpringBoot項目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫,需要的朋友可以參考下
    2024-07-07
  • @RefreshScope 自動刷新配置文件的實例講解

    @RefreshScope 自動刷新配置文件的實例講解

    efreshScope(org.springframework.cloud.context.scope.refresh)是spring cloud提供的一種特殊的scope實現(xiàn),用來實現(xiàn)配置、實例熱加載,這篇文章主要介紹了@RefreshScope 自動刷新配置文件,需要的朋友可以參考下
    2022-11-11
  • Java設(shè)計模式之代理模式原理及實現(xiàn)代碼分享

    Java設(shè)計模式之代理模式原理及實現(xiàn)代碼分享

    這篇文章主要介紹了Java設(shè)計模式之代理模式原理及實現(xiàn)代碼分享,設(shè)計代理模式的定義,靜態(tài)代理,動態(tài)代理,jdk動態(tài)代理實現(xiàn)步驟,原理及源碼等相關(guān)內(nèi)容,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • 關(guān)于Java中Bean的生命周期詳解

    關(guān)于Java中Bean的生命周期詳解

    這篇文章主要介紹了關(guān)于Java中Bean的生命周期詳解,所謂的?命周期指的是?個對象從誕?到銷毀的整個?命過程,我們把這個過程就叫做?個對象的?命周期,需要的朋友可以參考下
    2023-08-08
  • SpringBoot打成jar包瘦身方法總結(jié)

    SpringBoot打成jar包瘦身方法總結(jié)

    springBoot打包的時候代碼和jar包打包在同一個jar包里面,會導(dǎo)致jar包非常龐大,下面這篇文章主要給大家介紹了關(guān)于SpringBoot打的jar包瘦身方法的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • Java Hibernate中使用HQL語句進(jìn)行數(shù)據(jù)庫查詢的要點解析

    Java Hibernate中使用HQL語句進(jìn)行數(shù)據(jù)庫查詢的要點解析

    HQL是Hibernate框架中提供的關(guān)系型數(shù)據(jù)庫操作腳本,當(dāng)然我們也可以使用原生的SQL語句,這里我們來看一下在Java Hibernate中使用HQL語句進(jìn)行數(shù)據(jù)庫查詢的要點解析:
    2016-06-06
  • Java獲取Cookie里的指定值的實現(xiàn)方法

    Java獲取Cookie里的指定值的實現(xiàn)方法

    在Java中,我們經(jīng)常需要從HTTP請求中獲取Cookie,并從中提取特定的值,下面我們將介紹如何通過Java代碼獲取Cookie中的指定值,文章通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2024-09-09
  • 如何通過try-catch判斷數(shù)據(jù)庫唯一鍵字段是否重復(fù)

    如何通過try-catch判斷數(shù)據(jù)庫唯一鍵字段是否重復(fù)

    在MyBatis+MySQL中,通過try-catch捕獲唯一約束異常可避免重復(fù)數(shù)據(jù)查詢,優(yōu)點是減少數(shù)據(jù)庫交互、提升并發(fā)安全,缺點是異常處理開銷較高,適用于數(shù)據(jù)重復(fù)較少的場景,本文給大家介紹如何通過try-catch判斷數(shù)據(jù)庫唯一鍵字段是否重復(fù),感興趣的朋友一起看看吧
    2025-08-08
  • MyBatis實現(xiàn)動態(tài)查詢、模糊查詢功能

    MyBatis實現(xiàn)動態(tài)查詢、模糊查詢功能

    這篇文章主要介紹了MyBatis實現(xiàn)動態(tài)查詢、模糊查詢功能,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-06-06
  • jdk自帶定時器使用方法詳解

    jdk自帶定時器使用方法詳解

    這篇文章主要為大家詳細(xì)介紹了jdk自帶定時器的使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06

最新評論

阳城县| 古交市| 益阳市| 永吉县| 安图县| 富阳市| 和硕县| 深泽县| 青神县| 吉首市| 沙洋县| 华池县| 兴义市| 淮阳县| 晋中市| 美姑县| 邢台县| 开封县| 马关县| 义马市| 旬邑县| 南康市| 道孚县| 收藏| 蓝田县| 双桥区| 宾阳县| 新宾| 嫩江县| 阳朔县| 五家渠市| 南华县| 瑞安市| 濮阳县| 屏边| 仪征市| 河南省| 绿春县| 乌鲁木齐市| 翁牛特旗| 南丰县|