164岗憋、Spark SQL實(shí)戰(zhàn)開發(fā)進(jìn)階之新聞網(wǎng)站關(guān)鍵指標(biāo)離線統(tǒng)計

實(shí)戰(zhàn)背景

新聞網(wǎng)站

  1. 版塊
  2. 新聞頁面
  3. 新用戶注冊
  4. 用戶跳出

案例需求分析

  1. 每天每個頁面的PV
    PV是Page View皱埠,是指一個頁面被所有用戶訪問次數(shù)的總和熏版,頁面被訪問一次就被記錄1次PV
  2. 每天每個頁面的UV
    UV是User View畔柔,是指一個頁面被多少個用戶訪問了氯夷,一個用戶訪問一次是1次UV,一個用戶訪問多次還是1次UV
  3. 新用戶注冊比率
    當(dāng)天注冊用戶數(shù) / 當(dāng)天未注冊用戶的訪問數(shù)
  4. 用戶跳出率
    IP只瀏覽了一個頁面就離開網(wǎng)站的次數(shù)/網(wǎng)站總訪問數(shù)(PV)
  5. 版塊熱度排行榜
    根據(jù)每個版塊每天被訪問的次數(shù)靶擦,做出一個排行榜

網(wǎng)站日志格式

date timestamp userid pageid section action
日志字段說明
date: 日期腮考,yyyy-MM-dd格式
timestamp: 時間戳
userid: 用戶id
pageid: 頁面id
section: 新聞版塊
action: 用戶行為,兩類玄捕,點(diǎn)擊頁面和注冊

模擬數(shù)據(jù)生成程序

public class OfflineDataGenerator {
    public static void main(String[] args) throws Exception {
        StringBuffer buffer = new StringBuffer("");

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Random random = new Random();
        String[] sections = new String[] {"country", "international", "sport", "entertainment", "movie", "carton", "tv-show", "technology", "internet", "car"};

        int[] newOldUserArr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        // 生成日期踩蔚,默認(rèn)就是昨天
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DAY_OF_YEAR, -1);
        Date yesterday = cal.getTime();

        String date = sdf.format(yesterday);

        // 生成10000000條訪問數(shù)據(jù)
        for(int i = 0; i < 10000000; i++) {
            // 生成時間戳
            long timestamp = System.currentTimeMillis();

            // 生成隨機(jī)userid(默認(rèn)1000注冊用戶,每天1/10的訪客是未注冊用戶)
            Long userid = 0L;

            int newOldUser = newOldUserArr[random.nextInt(10)];
            if(newOldUser == 1) {
                userid = null;
            } else {
                userid = (long) random.nextInt(1000);
            }

            // 生成隨機(jī)pageid(總共1k個頁面)
            Long pageid = (long) random.nextInt(1000);

            // 生成隨機(jī)版塊(總共10個版塊)
            String section = sections[random.nextInt(10)];

            // 生成固定的行為枚粘,view
            String action = "view";

            buffer.append(date).append("?")
                    .append(timestamp).append("?")
                    .append(userid).append("?")
                    .append(pageid).append("?")
                    .append(section).append("?")
                    .append(action).append("\n");
        }

        // 生成100000條注冊數(shù)據(jù)
        for(int i = 0; i < 100000; i++) {
            // 生成時間戳
            long timestamp = System.currentTimeMillis();

            // 新用戶都是userid為null
            Long userid = null;

            // 生成隨機(jī)pageid馅闽,都是null
            Long pageid = null;

            // 生成隨機(jī)版塊,都是null
            String section = null;

            // 生成固定的行為赌结,view
            String action = "register";

            buffer.append(date).append("?")
                    .append(timestamp).append("?")
                    .append(userid).append("?")
                    .append(pageid).append("?")
                    .append(section).append("?")
                    .append(action).append("\n");
        }

        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new OutputStreamWriter(
                    new FileOutputStream("C:\\Users\\ZJ\\Desktop\\access.log")));
            pw.write(buffer.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pw.close();
        }
    }
}

創(chuàng)建相關(guān)表

在hive中創(chuàng)建訪問日志表

create table news (
date string,
timestamp bigint,
userid bigint,
pageid bigint,
section string,
action string);

將模擬數(shù)據(jù)導(dǎo)入hive表中

load data local inpath '/opt/spark-study/news.log' into table news;

編碼

main方法

 public static void main(String[] args) {
        SparkSession sparkSession = SparkSession.builder().appName("NewsOfflineStatSpark").enableHiveSupport().getOrCreate();

        String yesterday = getYesterday();
        
        // 開發(fā)第一個關(guān)鍵指標(biāo):頁面pv統(tǒng)計以及排序
        calculateDailyPagePv(sparkSession, yesterday);
        // 開發(fā)第二個關(guān)鍵指標(biāo):頁面uv統(tǒng)計以及排序
        calculateDailyPageUv(sparkSession, yesterday);
        // 開發(fā)第三個關(guān)鍵指標(biāo):新用戶注冊比率統(tǒng)計
        calculateDailyNewUserRegisterRate(sparkSession, yesterday);
        // 開發(fā)第四個關(guān)鍵指標(biāo):用戶跳出率統(tǒng)計
        calculateDailyUserJumpRate(sparkSession, yesterday);
        // 開發(fā)第五個關(guān)鍵指標(biāo):版塊熱度排行榜
        calculateDailySectionPvSort(sparkSession, yesterday);
    }

getYesterday方法

private static String getYesterday() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.add(Calendar.DAY_OF_YEAR, -1);

        Date yesterday = cal.getTime();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(yesterday);
    }
}

每天每個頁面的PV

private static void calculateDailyPagePv(SparkSession sparkSession, String yesterday) {
        // select date,pageid, pv from(
        //     select date,pageid,count(pageid) as pv from news where date = '2019-01-24' and action = 'view' group by date,pageid
        // ) t
        // order by pv desc;
        String sql =
                "select date, pageid, pv from ( " +
                    "select date, pageid, count(pageid) as pv from news " +
                    "where date = '" + yesterday + "' " +
                    " and action = " + "'view' " +
                    "group by date, pageid " +
                    ") t " +
                "order by pv desc";

        Dataset<Row> dataset = sparkSession.sql(sql);
        dataset.show();


    }

每天每個頁面的UV

private static void calculateDailyPageUv(SparkSession sparkSession, String yesterday) {
        // select date,pageid, uv from (
        //         select date, pageid, count(userid) as uv from (
        //             select date,pageid,userid from news where date = '2019-01-24' and action = 'view' group by date,pageid,userid
        //         ) t1
        //         group by date,pageid
        // ) t2
        // order by uv desc;

        String sql =
                "select date,pageid, uv from ( " +
                    "select date, pageid, count(userid) as uv from ( " +
                        "select date,pageid,userid from news " +
                        "where date = '" + yesterday + "' " +
                        "and action = 'view' " +
                        "group by date,pageid,userid " +
                    ") t1 " +
                    "group by date,pageid " +
                ") t2 " +
                "order by uv desc ";

        Dataset<Row> dataset = sparkSession.sql(sql);
        dataset.show();



    }

新用戶注冊比率

private static void calculateDailyNewUserRegisterRate(SparkSession sparkSession, String yesterday) {
        String sql1 = "SELECT count(*) FROM news WHERE action='view' AND date='" + yesterday + "' AND userid IS NULL";

        String sql2 = "SELECT count(*) FROM news WHERE action='register' AND date='" + yesterday + "' ";

        Dataset<Row> sql = sparkSession.sql(sql1);
        Long result1 = sql.collectAsList().get(0).getLong(0);
        long number1 = 0L;
        if(result1 != null) {
            number1 = result1;
        }
        Dataset<Row> sql3 = sparkSession.sql(sql2);
        Long result2 = sql3.collectAsList().get(0).getLong(0);
        long number2 = 0L;
        if(result2 != null) {
            number2 = result2;
        }

        // 計算結(jié)果
        System.out.println("======================" + number1 + "======================");
        System.out.println("======================" + number2 + "======================");
        double rate = (double)number2 / (double)number1;
        System.out.println("======================" + rate + "======================");

    }

用戶跳出率

private static void calculateDailyUserJumpRate(SparkSession sparkSession, String yesterday) {

        // 網(wǎng)站總訪問數(shù)
        String sql1 = "select count(*) from news where action='view' and date='" + yesterday + "' and userid is not null";

        // select date,userid,count(userid) as time from news where action='view' and date='2019-01-26' and userid is not null group by date,userid;
        // 已注冊用戶的昨天跳出的總數(shù)
        String sql2 =
                "select count(userid) from ( " +
                    "select date,userid,count(userid) as time from news where action='view' and date='" + yesterday + "' and userid is not null group by date,userid " +
                ") t " +
                "where time = 1";
        Dataset<Row> sql = sparkSession.sql(sql1);
        Long result1 = sql.collectAsList().get(0).getLong(0);
        long number1 = 0L;
        if(result1 != null) {
            number1 = result1;
        }
        Dataset<Row> sql3 = sparkSession.sql(sql2);
        Long result2 = sql3.collectAsList().get(0).getLong(0);
        long number2 = 0L;
        if(result2 != null) {
            number2 = result2;
        }

        // 計算結(jié)果
        System.out.println("======================" + number1 + "======================");
        System.out.println("======================" + number2 + "======================");
        double rate = (double)number2 / (double)number1;
        System.out.println("======================" + rate + "======================");
    }

版塊熱度排行榜

private static void calculateDailySectionPvSort(SparkSession sparkSession, String yesterday) {
        // select date,section,count(section) as num from news where action='view' and date='2019-01-25' group by date,section
        String sql =
                "select date,section,num from ( " +
                    "select date,section,count(section) as num from news where action='view' and date='" + yesterday + "' group by date,section " +
                ") t " +
                "order by num desc";
        Dataset<Row> sql1 = sparkSession.sql(sql);
        sql1.show();

    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捞蛋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子柬姚,更是在濱河造成了極大的恐慌拟杉,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件量承,死亡現(xiàn)場離奇詭異搬设,居然都是意外死亡穴店,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門拿穴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泣洞,“玉大人,你說我怎么就攤上這事默色∏蚧耍” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵腿宰,是天一觀的道長呕诉。 經(jīng)常有香客問我,道長吃度,這世上最難降的妖魔是什么甩挫? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮椿每,結(jié)果婚禮上伊者,老公的妹妹穿的比我還像新娘。我一直安慰自己间护,他們只是感情好亦渗,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著汁尺,像睡著了一般央碟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上均函,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天亿虽,我揣著相機(jī)與錄音,去河邊找鬼苞也。 笑死洛勉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的如迟。 我是一名探鬼主播收毫,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼殷勘!你這毒婦竟也來了此再?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤玲销,失蹤者是張志新(化名)和其女友劉穎输拇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贤斜,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡策吠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年逛裤,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片猴抹。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡带族,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蟀给,到底是詐尸還是另有隱情蝙砌,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布跋理,位于F島的核電站拍霜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏薪介。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一越驻、第九天 我趴在偏房一處隱蔽的房頂上張望汁政。 院中可真熱鬧,春花似錦缀旁、人聲如沸记劈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽目木。三九已至,卻和暖如春懊渡,著一層夾襖步出監(jiān)牢的瞬間刽射,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工剃执, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留誓禁,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓肾档,卻偏偏與公主長得像摹恰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子怒见,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容