HBase分佈式數據庫——穀粒微博實戰

第一章 需求分析

  1. 微博內容的瀏覽,數據庫表設計
  2. 用戶社交體現:關注用戶,取關用戶
  3. 拉取關注的人的微博內容

第二章 數據庫設計

 設計成三張表微博內容表、用戶關係表和微博收件箱表。

 

第三章 代碼實現

3.1 創建工程

maven工程依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>HBase</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>guli-weibo</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>1.3.1</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>

工程文件夾:

 

 

3.2 constants包

在這裏我們定義的是一些常量,例如是命名空間、表明、列名等,以便後面我們需要使用的時候反覆重寫,防止寫錯,也可以進行一個解耦合。

public class Constants {
    public static Configuration CONFIGURATION = null;
    // HBase setting 這裏不知道爲什麼要這要寫纔可以,視頻裏面沒有寫這個也可以運行,
    // 但是我就會報錯,說無法連接,希望有大佬可以解釋一下。
    // 那個set裏面第二個放的是你們集羣的名稱,可能會不一樣,看你們自己的命名了。
    static {
        CONFIGURATION = HBaseConfiguration.create();
        CONFIGURATION.set("hbase.zookeeper.quorum", "hadoop102,hadoop103,hadoop104");
    }
    // 命名空間
    public static final String NAMESPACE = "weibo";
    // weibo內容表
    public static final String CONTENT_TABLE = "weibo:content";
    public static final String CONTENT_TABLE_CF = "info";
    public static final int CONTENT_TABLE_VERSIONS = 1;
    // 用戶關係表
    public static final String RELATION_TABLE = "weibo:relation";
    // 第一個列族
    public static final String RELATION_TABLE_CF1 = "attends";
    // 第二個列族
    public static final String RELATION_TABLE_CF2 = "fans";
    public static final int RELATION_TABLE_VERSIONS = 1;
    //收件箱表
    public static final String INBOX_TABLE = "weibo:inbox";
    public static final String INBOX_TABLE_CF = "info";
    public static final int INBOX_TABLE_VERSIONS = 2;
}

3.3 utils包

這裏主要是進行DDL的操作,例如創建命名空間,以及對錶之類的操作。

public class HBaseUtil {
    // 1 創建命名空間
    public static void createNameSpace(String namespace) throws IOException {
    }
    // 2 判斷表是否存在
    private static boolean isTableExist(String tableName) throws IOException {
    }
    // 3 創建表
    public static void createTable(String tableName, int versions, String... cfs) throws IOException {
    }
}

創建命名空間

// 1 創建命名空間
    public static void createNameSpace(String namespace) throws IOException {
        // 1 獲取Connection對象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 2 獲取admin對象
        Admin admin = connection.getAdmin();
        // 3 構建命名空間描述器
        NamespaceDescriptor build = NamespaceDescriptor.create(namespace).build();
        // 4 創建命名空間
        admin.createNamespace(build);
        // 5 關閉資源
        admin.close();
        connection.close();
    }

判斷表是否存在

 // 2 判斷表是否存在
    private static boolean isTableExist(String tableName) throws IOException {
        // 1 獲取Connection對象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 2 獲取admin對象
        Admin admin = connection.getAdmin();
        // 3 判斷是否存在
        boolean result = admin.tableExists(TableName.valueOf(tableName));
        // 4 關閉資源
        admin.close();
        connection.close();
        // 5 返回結果
        return result;
    }

創建表

 // 3 創建表
    public static void createTable(String tableName, int versions, String... cfs) throws IOException {
        // 1 判斷是否傳入了列族信息
        if (cfs.length <= 0) {
            System.out.println("column information is not available.");
            return ;
        }
        // 2 判斷表是否存在
        if (isTableExist(tableName)) {
            System.out.println(tableName + "table is exist.");
            return;
        }
        // 3 獲取Connection對象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 4 獲取Admin對象
        Admin admin = connection.getAdmin();
        // 5 創建表描述器
        HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
        // 6 循環添加列族信息
        for (String cf : cfs) {
            HColumnDescriptor hColumnDescriptor = new HColumnDescriptor(cf);
            // 7 設置版本
            hColumnDescriptor.setMaxVersions(versions);
            hTableDescriptor.addFamily(hColumnDescriptor);
        }
        // 8 創建表操作
        admin.createTable(hTableDescriptor);
        // 關閉資源
        admin.close();
        connection.close();
    }

3.4 dao包

這裏是一些DML的操作,例如一些在表中插入數據,或者是刪除數據的操作。

public class HBaseDao {
    // 1 發佈微博
    public static void publishWeiBo(String uid, String content) throws IOException {
    }
    // 2 關注用戶
    public static void addAttends(String uid, String... attends) throws IOException {
    }
    // 3 取關
    public static void deleteAttends(String uid, String... dels) throws IOException {
    }
    // 4 獲取
    public static void getInit(String uid) throws IOException {
    }
    // 5 獲取某個人的所有微博詳情
    public static void getWeibo(String uid) throws IOException {
    }
}

發微博功能

這一個功能比較難,因爲不僅對於當前發佈微博的人的內容表需要進行更新,並且需要對當前這個人的粉絲的收件表也需要更新,因爲粉絲需要看到關注的人的最新動態。所以這裏需要對兩張表,多個人進行操作。

// 1 發佈微博
    public static void publishWeiBo(String uid, String content) throws IOException {
        // 1 獲取Connection對象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 2 get the admin object
        Admin admin = connection.getAdmin();
        // 第一部分:操縱微博內容表
        // 1.獲取微博內容表對象
        Table conTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        // 2. 獲取當前時間戳
        long ts = System.currentTimeMillis();
        // 3 獲取Rowkey
        String rowKey = uid + "_" + ts;
        // 4 創建Put對象
        Put conPut = new Put(Bytes.toBytes(rowKey));
        // 5 給Put對象賦值
        conPut.addColumn(Bytes.toBytes(Constants.CONTENT_TABLE_CF), Bytes.toBytes("content"), Bytes.toBytes(content));
        // 6 執行插入數據操作
        conTable.put(conPut);
        // 第二部分:操縱微博收件箱表
        // 1 獲取用戶關係表對象
        Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
        // 2 獲取當前發佈微博人的fans列族數據
        Get get = new Get(Bytes.toBytes(uid));
        // 指定relation關係表的第二個列族
        get.addFamily(Bytes.toBytes(Constants.RELATION_TABLE_CF2));
        Result result = relaTable.get(get);
        // 3 創建一個集合,用於存放微博內容表的put對象
        ArrayList<Put> inboxPuts = new ArrayList<>();
        // 4 遍歷粉絲
        for (Cell cell : result.rawCells()) {
            // 5 構建微博收件箱表的Put對象
            Put inboxPut = new Put(CellUtil.cloneQualifier(cell));
            // 6 給收件箱表的Put對象賦值
            inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(uid), Bytes.toBytes(rowKey));
            // 7 將收件箱表的Put對象存入集合
            inboxPuts.add(inboxPut);
        }
        // 8 判斷是否有粉絲
        if (inboxPuts.size() > 0) {
            // 獲取收件箱表對象
            Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
            // 執行收件箱表數據插入操作
            inboxTable.put(inboxPuts);
            // 關閉收件箱表
            inboxTable.close();
        }
        // 關閉資源
        relaTable.close();
        conTable.close();
        connection.close();
    }

關注功能

關注某個人之後,需要獲得這個人的最近發佈的微博,以及在微博關係表當中需要添加新的關係。

  // 2 關注用戶
    public static void addAttends(String uid, String... attends) throws IOException {
        // 校驗是否添加了待關注的人
        if (attends.length <= 0) {
            System.out.println("please choose attention person");
            return ;
        }
        // 獲取Connection對象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 第一部分:操作用戶關係表
        // 1 獲取用戶關係表對象
        Table relaTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
        // 2 創建一個集合,用於存放用戶關係表的Put對象
        ArrayList<Put> relaPuts  = new ArrayList<>();
        // 3 創建操作者的Put對象
        Put uidPut = new Put(Bytes.toBytes(uid));
        // 4 循環創建被關注者的Put對象
        for (String attend : attends) {
            // 5 給操作者的Put對象賦值
            uidPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(attend), Bytes.toBytes(attend));
            // 6 創建被關注者的Put對象
            Put attendPut = new Put(Bytes.toBytes(attend));
            // 7 給被關注者的Put對象賦值
            attendPut.addColumn(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(uid), Bytes.toBytes(uid));
            // 8 將被關注者的Put對象放入集合
            relaPuts.add(attendPut);
        }
        // 9 將操作者的Put對象添加至集合
        relaPuts.add(uidPut);
        // 10 執行用戶關係表的插入數據操作
        relaTable.put(relaPuts);
        // 第二部分:操作收件箱表
        // 1 獲取微博內容表對象
        Table contTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        // 2 創建收件箱表的Put對象
        Put inboxPut = new Put(Bytes.toBytes(uid));
        // 3 循環attends,獲取每個被關注者的近期發佈的微博
        for (String attend : attends) {
            // 4 獲取當前被關注者的近期發佈的微博(scan)->集合
            // 這裏獲取只能用scan,因爲get要指定rowKey,而在關注時是不知道rowKey的時間戳
            // 不能全表掃描 這裏使用startRow 和stopRow
            Scan scan = new Scan(Bytes.toBytes(attend + "_"), Bytes.toBytes(attend + "|"));
            ResultScanner resultScanner = contTable.getScanner(scan);
            // define a time stamp
            long ts = System.currentTimeMillis();
            // 5 對獲取的值進行遍歷
            // 按rowKey的比較規則時間戳最小的微博最先出來,先放入列中,最後才能拿到最新的
            // 這裏其實有問題 如果關注的人發佈太多的微博,只爲了得到最新的三天數據。而去插入大量無用的數據
            // 可以在發佈微博函數中反轉時間戳解決
            for (Result result : resultScanner) {
                // 6 給收件箱表的Put對象賦值
                inboxPut.addColumn(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(attend), ts++, result.getRow());
            }
        }
        // 7 判斷當前的Put對象是否爲空
        if (!inboxPut.isEmpty()) {
            // 獲取收件箱表對象
            Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
            // 插入數據
            inboxTable.put(inboxPut);
            // 關閉收件箱表連接
            inboxTable.close();
        }
        // 關閉資源
        relaTable.close();
        contTable.close();
        connection.close();
    }

取消關注

// 3 取關
    public static void deleteAttends(String uid, String... dels) throws IOException {

        if (dels.length <= 0) {
            System.out.println("please choose deletion person");
            return;
        }
        // 獲取Connection對象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 第一部分:操作用戶關係表
        // 1 獲取用戶關係表對象
        Table relatTable = connection.getTable(TableName.valueOf(Constants.RELATION_TABLE));
        // 2 創建一個集合,用戶存放用戶關係表的Delete對象
        ArrayList<Delete> relatDelete = new ArrayList<>();
        // 3 創建操作者的Delete對象
        Delete uidDelete = new Delete(Bytes.toBytes(uid));
        // 4 循環創建被取關者的Delete對象
        for (String del : dels) {
            // 5 給操作者的Delete對象賦值
            uidDelete.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_CF1), Bytes.toBytes(del));
            // 6 創建被取關者的Delete對象
            Delete delDelte = new Delete(Bytes.toBytes(del));
            // 7 給被取關者的Delete對象賦值
            delDelte.addColumns(Bytes.toBytes(Constants.RELATION_TABLE_CF2), Bytes.toBytes(uid));
            // 8 將被取關者的Delete對象添加至集合
            relatDelete.add(delDelte);
        }
        // 9 將操作者的Delete對象添加至集合
        relatDelete.add(uidDelete);
        // 10 執行用戶關係表的刪除操作
        relatTable.delete(relatDelete);
// 第二部分:操作收件箱表 // 1 獲取收件箱表對象 Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE)); // 2 創建操作者的Delete對象 Delete inboxDelete = new Delete(Bytes.toBytes(uid)); // 3 給操作者的Delete對象賦值 for (String del : dels) { inboxDelete.addColumns(Bytes.toBytes(Constants.INBOX_TABLE_CF), Bytes.toBytes(del)); } // 4 執行收件箱表的刪除操作 inboxTable.delete(inboxDelete); // 關閉資源 relatTable.close(); inboxTable.close(); connection.close(); }

獲得用戶初始頁

 // 4 獲取
    public static void getInit(String uid) throws IOException {
        // 1 獲取Connection對象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 2 獲取收件箱表對象
        Table inboxTable = connection.getTable(TableName.valueOf(Constants.INBOX_TABLE));
        // 3 獲取微博內容表對象
        Table conTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        // 4 創建收件箱表Get對象,並獲取數據(設置最大版本)
        Get inboxGet = new Get(Bytes.toBytes(uid));
        inboxGet.setMaxVersions();
        Result result = inboxTable.get(inboxGet);
        // 5 遍歷獲取的數據
        for (Cell cell : result.rawCells()) {

            // 6 構建微博內容表Get對象
            Get conGet = new Get(CellUtil.cloneValue(cell));

            // 7 獲取Get對象的數據內容
            Result conResult = conTable.get(conGet);

            // 8 解析內容並打印
            for (Cell conCell : conResult.rawCells()) {
                System.out.println("RK:" + Bytes.toString(CellUtil.cloneRow(conCell)) +
                        ", CF:" + Bytes.toString(CellUtil.cloneFamily(conCell)) +
                        ", CN:" + Bytes.toString(CellUtil.cloneQualifier(conCell)) +
                        ", Value:" + Bytes.toString(CellUtil.cloneValue(conCell)));
            }
        }
        // 9 關閉資源
        inboxTable.close();
        conTable.close();
        connection.close();
    }

獲得用戶全部微博內容

 // 5 獲取某個人的所有微博詳情
    public static void getWeibo(String uid) throws IOException {
        // 1 獲取Connection對象
        Connection connection = ConnectionFactory.createConnection(Constants.CONFIGURATION);
        // 2 獲取微博內容表對象
        Table conTable = connection.getTable(TableName.valueOf(Constants.CONTENT_TABLE));
        // 構建Scan對象
        Scan scan = new Scan();
        // 構建過濾器
        RowFilter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator(uid + "_"));
        scan.setFilter(rowFilter);
        // 4 獲取數據
        ResultScanner resultScanner = conTable.getScanner(scan);
        // 5 解析數據並打印
        for (Result result : resultScanner) {
            for (Cell cell : result.rawCells()) {
                System.out.println("RK:" + Bytes.toString(CellUtil.cloneRow(cell)) +
                        ", CF:" + Bytes.toString(CellUtil.cloneFamily(cell)) +
                        ", CN:" + Bytes.toString(CellUtil.cloneQualifier(cell)) +
                        ", Value:" + Bytes.toString(CellUtil.cloneValue(cell)));
            }
        }
        // 6 關閉資源
        conTable.close();
        connection.close();
    }

3.5 test包 測試

public class TestWeiBo {

    public static void init() {

        try {
            // 創建命名空間
            HBaseUtil.createNameSpace(Constants.NAMESPACE);
            // 創建微博內容表
            HBaseUtil.createTable(Constants.CONTENT_TABLE, Constants.CONTENT_TABLE_VERSIONS, Constants.CONTENT_TABLE_CF);
            // 創建用戶關係表
            HBaseUtil.createTable(Constants.RELATION_TABLE, Constants.RELATION_TABLE_VERSIONS,
                    Constants.RELATION_TABLE_CF1, Constants.RELATION_TABLE_CF2);
            // 創建收件箱表
            HBaseUtil.createTable(Constants.INBOX_TABLE, Constants.INBOX_TABLE_VERSIONS, Constants.INBOX_TABLE_CF);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        // 初始化
        init();
        System.out.println("init done");

        // 1001 發佈微博
        HBaseDao.publishWeiBo("1001","這是1001的微博,Hello World");
        System.out.println("publish done");

        // 1002 關注 1001, 1003
        HBaseDao.addAttends("1002", "1001", "1003");
        System.out.println("attend done");

        // 獲取1002 初始化頁面
        HBaseDao.getInit("1002");
        System.out.println("*********************111**********************");

        // 1003發佈三條微博 同時1001發佈兩條
        HBaseDao.publishWeiBo("1003", "誰說的趕緊下課!!");
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1001", "我沒說話!!");
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1003", "誰說的!!");
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1001", "反正飛機是下線了!!");
        Thread.sleep(10);
        HBaseDao.publishWeiBo("1003", "你們愛咋咋地!!");

        // 獲取1002 初始頁面
        HBaseDao.getInit("1002");
        System.out.println("*********************222***********************");
        // 1002 取關 1003
        HBaseDao.deleteAttends("1002", "1003");
        //獲取1002初始化頁面
        HBaseDao.getInit("1002");
        System.out.println("*********************333***********************");
        // 1002再次關注1003
        HBaseDao.addAttends("1002", "1003");
        // 獲取1002初始化頁面
        HBaseDao.getInit("1002");
        System.out.println("*********************444***********************");
        // get 1001 detail initial page
        HBaseDao.getWeibo("1001");
        System.out.println("*********************555***********************");
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章