偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

如何使用 Redis 完成 PV,UV 統(tǒng)計(jì)?

開發(fā)
本文我們分析了如何使用 Redis 統(tǒng)計(jì) PV 和 UV,通過 Redis 的 INCR? 和 HyperLogLog 數(shù)據(jù)結(jié)構(gòu),可以高效地實(shí)現(xiàn) PV 和 UV 的統(tǒng)計(jì)。

面試中,我們經(jīng)常會(huì)被問題 PV,UV,那么,什么是 PV?什么又是UV?如何使用 Redis 統(tǒng)計(jì) PV 和 UV?這篇文章,我們將詳細(xì)介紹如何在 Java 中使用 Redis 實(shí)現(xiàn) PV 和 UV 的統(tǒng)計(jì)。

1. 什么是 PV 和 UV?

  • PV(Page Views):指頁面被訪問的總次數(shù)。每一次頁面加載或刷新都會(huì)增加一次 PV,無論訪問者是誰。
  • UV(Unique Visitors):指獨(dú)立訪客數(shù)。通常通過用戶的唯一標(biāo)識(shí)(如用戶 ID、IP 地址、Cookie 等)來統(tǒng)計(jì)同一用戶在一定時(shí)間范圍內(nèi)的訪問次數(shù),確保每個(gè)獨(dú)立訪客只計(jì)數(shù)一次。

2. Redis 如何統(tǒng)計(jì) PV 和 UV?

(1) 統(tǒng)計(jì) PV

統(tǒng)計(jì) PV 可以通過 Redis 的 INCR 命令實(shí)現(xiàn)。這是一個(gè)原子操作,可以確保在高并發(fā)情況下準(zhǔn)確計(jì)數(shù)。

(2) 統(tǒng)計(jì) UV

統(tǒng)計(jì) UV 可以使用 Redis 的 HyperLogLog 或 Bitmap 數(shù)據(jù)結(jié)構(gòu):

  • HyperLogLog:適合大規(guī)模去重統(tǒng)計(jì),占用內(nèi)存小,但只能估算基數(shù),誤差約為 0.81%。
  • Bitmap:通過位圖記錄用戶訪問情況,適合用戶 ID 范圍固定且不大的場(chǎng)景。

本示例中將使用 HyperLogLog 來統(tǒng)計(jì) UV,因?yàn)樗m用于大規(guī)模和動(dòng)態(tài)用戶場(chǎng)景,且實(shí)現(xiàn)簡單。

(3) 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)

假設(shè)我們要統(tǒng)計(jì)某個(gè)頁面(例如 /home)每日的 PV 和 UV,可以設(shè)計(jì)如下 Redis 鍵:

  • pv:home:20250301 — 存儲(chǔ) /home 頁面在 2025年3月1日的 PV 計(jì)數(shù)。
  • uv:home:20250301 — 存儲(chǔ) /home 頁面在 2025年3月1日的 UV 計(jì)數(shù)。

3. 示例代碼

為了更好地理解如何使用 Redis統(tǒng)計(jì) PV,UV,確保在項(xiàng)目中添加 Jedis 依賴。

(1) 示例代碼:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

publicclass RedisPvUvCounter {
    // Redis 服務(wù)器配置
    privatestaticfinal String REDIS_HOST = "localhost";
    privatestaticfinalint REDIS_PORT = 6379;
    privatestaticfinal String PAGE_NAME = "home"; // 頁面名稱
    privatestaticfinal DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");

    private JedisPool jedisPool;

    // 構(gòu)造方法,初始化 Jedis 連接池
    public RedisPvUvCounter() {
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(128); // 最大連接數(shù),可根據(jù)需要調(diào)整
        this.jedisPool = new JedisPool(poolConfig, REDIS_HOST, REDIS_PORT);
    }

    /**
     * 統(tǒng)計(jì) PV
     * @param pageName 頁面名稱
     */
    public void incrementPv(String pageName) {
        String date = LocalDate.now().format(DATE_FORMATTER);
        String pvKey = String.format("pv:%s:%s", pageName, date);
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.incr(pvKey);
        }
    }

    /**
     * 統(tǒng)計(jì) UV
     * @param pageName 頁面名稱
     * @param userId   用戶唯一標(biāo)識(shí)
     */
    public void addUv(String pageName, String userId) {
        String date = LocalDate.now().format(DATE_FORMATTER);
        String uvKey = String.format("uv:%s:%s", pageName, date);
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.pfadd(uvKey, userId);
        }
    }

    /**
     * 獲取 PV 統(tǒng)計(jì)
     * @param pageName 頁面名稱
     * @return PV 數(shù)量
     */
    public long getPv(String pageName) {
        String date = LocalDate.now().format(DATE_FORMATTER);
        String pvKey = String.format("pv:%s:%s", pageName, date);
        try (Jedis jedis = jedisPool.getResource()) {
            String pvStr = jedis.get(pvKey);
            return pvStr != null ? Long.parseLong(pvStr) : 0;
        }
    }

    /**
     * 獲取 UV 統(tǒng)計(jì)
     * @param pageName 頁面名稱
     * @return UV 數(shù)量
     */
    public long getUv(String pageName) {
        String date = LocalDate.now().format(DATE_FORMATTER);
        String uvKey = String.format("uv:%s:%s", pageName, date);
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.pfcount(uvKey);
        }
    }

    /**
     * 設(shè)置鍵的過期時(shí)間(例如 2 天后過期)
     * @param key キー
     * @param seconds 秒數(shù)
     */
    public void setExpire(String key, int seconds) {
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.expire(key, seconds);
        }
    }

    /**
     * 關(guān)閉 Jedis 連接池
     */
    public void close() {
        if (jedisPool != null) {
            jedisPool.close();
        }
    }

    public static void main(String[] args) {
        RedisPvUvCounter counter = new RedisPvUvCounter();

        String page = "home";
        String user1 = "user_001";
        String user2 = "user_002";

        // 模擬 PV 和 UV 統(tǒng)計(jì)
        counter.incrementPv(page);
        counter.addUv(page, user1);

        counter.incrementPv(page);
        counter.addUv(page, user1); // 重復(fù)訪問,不增加 UV

        counter.incrementPv(page);
        counter.addUv(page, user2);

        // 設(shè)置鍵的過期時(shí)間(可選,根據(jù)實(shí)際需求)
        String date = LocalDate.now().format(DATE_FORMATTER);
        String pvKey = String.format("pv:%s:%s", page, date);
        String uvKey = String.format("uv:%s:%s", page, date);
        counter.setExpire(pvKey, 2 * 24 * 60 * 60); // PV 鍵 2 天后過期
        counter.setExpire(uvKey, 2 * 24 * 60 * 60); // UV 鍵 2 天后過期

        // 獲取統(tǒng)計(jì)結(jié)果
        long pv = counter.getPv(page);
        long uv = counter.getUv(page);

        System.out.println("PV 總數(shù): " + pv); // 輸出: PV 總數(shù): 3
        System.out.println("UV 總數(shù): " + uv); // 輸出: UV 總數(shù): 2

        // 關(guān)閉連接池
        counter.close();
    }
}

(2) 代碼詳解

①連接 Redis

使用 JedisPool 來管理 Redis 連接池,提升性能和資源利用率。通過配置 JedisPoolConfig 可以調(diào)整連接池的相關(guān)參數(shù),如最大連接數(shù)等。

②統(tǒng)計(jì) PV

  • 使用 INCR 命令對(duì) PV 鍵進(jìn)行自增。
  • 鍵的命名規(guī)范為 pv:{pageName}:{date}(例如 pv:home:20250301)。
  • 每訪問一次頁面,調(diào)用 incrementPv 方法即可增加 PV 計(jì)數(shù)。

③統(tǒng)計(jì) UV

  • 使用 PFADD 命令將用戶的唯一標(biāo)識(shí)添加到 HyperLogLog 結(jié)構(gòu)中。
  • 鍵的命名規(guī)范為 uv:{pageName}:{date}(例如 uv:home:20250301)。
  • userId 可以是用戶的登錄 ID、IP 地址或其他唯一標(biāo)識(shí)。
  • HyperLogLog 會(huì)自動(dòng)去重,因此即使同一個(gè)用戶多次訪問,也只會(huì)計(jì)數(shù)一次。

④獲取 PV 和 UV 數(shù)量

  • PV 使用 GET 命令獲取鍵的值,并轉(zhuǎn)換為 long 類型。如果鍵不存在,則返回 0。
  • UV 使用 PFCOUNT 命令獲取 HyperLogLog 的估算基數(shù)。

⑤設(shè)置鍵的過期時(shí)間

為了避免 Redis 中存儲(chǔ)過多歷史數(shù)據(jù),可以為 PV 和 UV 鍵設(shè)置過期時(shí)間。本示例中設(shè)置為 2 天后過期??梢愿鶕?jù)實(shí)際需求調(diào)整。

⑥關(guān)閉連接池

使用完畢后,調(diào)用 close 方法關(guān)閉 JedisPool,釋放資源。

(3) 運(yùn)行示例

運(yùn)行 main 方法后,將模擬以下操作:

  • 用戶 user_001 訪問 /home 頁面,PV 增加 1,UV 增加 1。
  • 用戶 user_001 再次訪問 /home 頁面,PV 增加 1,UV 不變。
  • 用戶 user_002 訪問 /home 頁面,PV 增加 1,UV 增加 1。

最終輸出:

PV 總數(shù): 3
UV 總數(shù): 2

4. 擴(kuò)展與優(yōu)化

(1) 設(shè)置鍵的過期時(shí)間

可以在 incrementPv 和 addUv 方法中設(shè)置鍵的過期時(shí)間,以自動(dòng)刪除過期數(shù)據(jù),避免 Redis 內(nèi)存不斷增長。

public void incrementPv(String pageName) {
    String date = LocalDate.now().format(DATE_FORMATTER);
    String pvKey = String.format("pv:%s:%s", pageName, date);
    try (Jedis jedis = jedisPool.getResource()) {
        jedis.incr(pvKey);
        jedis.expire(pvKey, 2 * 24 * 60 * 60); // 設(shè)置過期時(shí)間為2天
    }
}

public void addUv(String pageName, String userId) {
    String date = LocalDate.now().format(DATE_FORMATTER);
    String uvKey = String.format("uv:%s:%s", pageName, date);
    try (Jedis jedis = jedisPool.getResource()) {
        jedis.pfadd(uvKey, userId);
        jedis.expire(uvKey, 2 * 24 * 60 * 60); // 設(shè)置過期時(shí)間為2天
    }
}

(2) 使用 Lua 腳本優(yōu)化

為了減少 Redis 交互次數(shù),可以使用 Lua 腳本將多個(gè)命令合并為一個(gè)原子操作。例如,可以在一次 Lua 腳本中同時(shí)對(duì) PV 和 UV 進(jìn)行操作。

(3) 分布式環(huán)境下的 Redis 集群

在分布式系統(tǒng)中,可以使用 Redis 集群來提高可用性和擴(kuò)展性。Jedis 提供了 JedisCluster 類來支持 Redis 集群。

(4) 選擇合適的唯一標(biāo)識(shí)

為了準(zhǔn)確統(tǒng)計(jì) UV,選擇唯一標(biāo)識(shí)非常關(guān)鍵。常見的方式包括:

  • 用戶登錄 ID:最可靠,但僅適用于已認(rèn)證用戶。
  • IP 地址:簡單但可能不夠準(zhǔn)確,受 NAT 和代理影響。
  • Cookie:通過生成唯一的 Cookie 標(biāo)識(shí)符,即使用戶未登錄也可以追蹤。

根據(jù)業(yè)務(wù)需求選擇合適的方式,并注意隱私和數(shù)據(jù)保護(hù)。

(5) 持久化與備份

確保 Redis 的持久化機(jī)制(RDB 或 AOF)已正確配置,以防止數(shù)據(jù)丟失。

5. 總結(jié)

本文,我們分析了如何使用 Redis 統(tǒng)計(jì) PV 和 UV,通過 Redis 的 INCR 和 HyperLogLog 數(shù)據(jù)結(jié)構(gòu),可以高效地實(shí)現(xiàn) PV 和 UV 的統(tǒng)計(jì)。另外,實(shí)際工作中,我們可以根據(jù)實(shí)際業(yè)務(wù)需求,可以進(jìn)一步優(yōu)化和擴(kuò)展,如設(shè)置鍵過期時(shí)間、使用 Lua 腳本、部署 Redis 集群等。

責(zé)任編輯:趙寧寧 來源: 猿java
相關(guān)推薦

2019-10-17 09:25:56

Spark StreaPVUV

2021-08-08 22:08:41

Redis開發(fā)網(wǎng)頁

2016-10-16 13:48:54

多維分析 UVPV

2021-11-01 13:11:45

FlinkPvUv

2021-06-03 08:10:30

SparkStream項(xiàng)目Uv

2021-06-06 13:10:12

FlinkPvUv

2015-12-23 17:08:25

H5

2015-12-14 14:26:56

Linux命令pv

2025-02-13 11:11:53

Redis哨兵代碼

2023-03-08 08:13:33

Pv工具

2017-12-22 10:34:02

大數(shù)據(jù)AI存儲(chǔ)

2025-06-09 08:21:55

2024-10-06 12:50:25

2017-12-18 09:02:42

Red Hat SysAnsible集成

2018-12-05 09:00:00

RedisRedis Strea數(shù)據(jù)庫

2021-08-04 17:55:38

keysRedis數(shù)據(jù)庫

2025-03-03 10:25:10

2012-03-28 14:06:43

軟件系統(tǒng)系統(tǒng)測(cè)試

2024-12-17 15:39:33

2024-03-22 12:10:39

Redis消息隊(duì)列數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)