Bigtable ו-Dataflow: אומנות למעקב אחר מסדי נתונים (HBase Java Client)

1. מבוא

ב-Codelab הזה, תשתמשו בכלי המעקב של Cloud Bigtable כדי ליצור יצירות אומנות שונות באמצעות כתיבה וקריאה של נתונים באמצעות Cloud Dataflow ולקוח Java HBase.

כאן מוסבר איך

  • טעינת כמויות גדולות של נתונים ל-Bigtable באמצעות Cloud Dataflow
  • מעקב אחר טבלאות ומופעים ב-Bigtable בזמן הטמעת הנתונים
  • שליחת שאילתה ל-Bigtable באמצעות משימת Dataflow
  • היכרות עם כלי הוויזואליזציה העיקרי שבו ניתן להשתמש כדי למצוא נקודות חמות בגלל עיצוב הסכימה
  • יצירת אומנות באמצעות כלי הוויזואליזציה העיקרי

d098cc81f78f02eb.png

איזה דירוג מגיע לדעתך לחוויית השימוש ב-Cloud Bigtable?

מתחילים בינונית בקיאים

איך תשתמשו במדריך הזה?

לקריאה בלבד לקרוא אותו ולבצע את התרגילים

2. יצירת מסד נתונים ב-Bigtable

Cloud Bigtable הוא שירות מסד הנתונים NoSQL מסוג Big Data של Google. מדובר באותו מסד נתונים שעליה מבוססים שירותי הליבה רבים של Google, כולל חיפוש Google, Analytics, מפות Google ו-Gmail. הוא אידיאלי להרצת עומסי עבודה אנליטיים גדולים ולפיתוח אפליקציות עם זמן אחזור קצר. מומלץ לקרוא מבוא מפורט במאמר מבוא ל-Cloud Bigtable Codelab.

יצירת פרויקט

קודם כול, יוצרים פרויקט חדש. משתמשים ב-Cloud Shell המובנה, שפותחים בלחיצה על "Activate Cloud Shell". בפינה השמאלית העליונה.

a74d156ca7862b28.png

כדי שיהיה קל יותר להעתיק ולהדביק את פקודות Codelab, מגדירים את משתני הסביבה הבאים:

BIGTABLE_PROJECT=$GOOGLE_CLOUD_PROJECT
INSTANCE_ID="keyviz-art-instance"
CLUSTER_ID="keyviz-art-cluster"
TABLE_ID="art"
CLUSTER_NUM_NODES=1
CLUSTER_ZONE="us-central1-c" # You can choose a zone closer to you

Cloud Shell מגיע עם הכלים שתשתמש בהם ב-Codelab הזה, כלי שורת הפקודה של gcloud, ממשק שורת הפקודה cbt, ו-Maven, שכבר מותקנים.

מריצים את הפקודה הזו כדי להפעיל את ממשקי Cloud Bigtable API.

gcloud services enable bigtable.googleapis.com bigtableadmin.googleapis.com

כדי ליצור מכונה, מריצים את הפקודה הבאה:

gcloud bigtable instances create $INSTANCE_ID \
    --cluster=$CLUSTER_ID \
    --cluster-zone=$CLUSTER_ZONE \
    --cluster-num-nodes=$CLUSTER_NUM_NODES \
    --display-name=$INSTANCE_ID

אחרי שיוצרים את המכונה, מריצים את הפקודות הבאות כדי לאכלס את קובץ התצורה cbt ואז ליצור משפחת טבלאות ועמודות:

echo project = $GOOGLE_CLOUD_PROJECT > ~/.cbtrc
echo instance = $INSTANCE_ID >> ~/.cbtrc

cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf

3. למידה: כתיבה ב-Bigtable באמצעות Dataflow

עקרונות בסיסיים בכתיבה

כשכותבים ב-Cloud Bigtable, צריך לספק אובייקט של הגדרת CloudBigtableTableConfiguration. האובייקט הזה מציין את מזהה הפרויקט ומזהה המכונה של הטבלה, וגם את שם הטבלה עצמה:

CloudBigtableTableConfiguration bigtableTableConfig =
    new CloudBigtableTableConfiguration.Builder()
        .withProjectId(PROJECT_ID)
        .withInstanceId(INSTANCE_ID)
        .withTableId(TABLE_ID)
        .build();

לאחר מכן צינור עיבוד הנתונים יכול להעביר אובייקטים מסוג Mutation מסוג HBase, כולל Put ו-Delete.

p.apply(Create.of("hello", "world"))
    .apply(
        ParDo.of(
            new DoFn<String, Mutation>() {
              @ProcessElement
              public void processElement(@Element String rowkey, OutputReceiver<Mutation> out) {
                long timestamp = System.currentTimeMillis();
                Put row = new Put(Bytes.toBytes(rowkey));

                row.addColumn(...);
                out.output(row);
              }
            }))
    .apply(CloudBigtableIO.writeToTable(bigtableTableConfig));

המשימה של LoadData Dataflow

בדף הבא נסביר איך להריץ את המשימה LoadData, אבל כאן אספר על החלקים החשובים בצינור עיבוד הנתונים.

כדי ליצור נתונים, צריך ליצור צינור עיבוד נתונים שמשתמש במחלקה GenerateSequence (בדומה ללולאת for) כדי לכתוב מספר שורות עם כמה מגה-בייט של נתונים אקראיים. מקש השורה יהיה המספר הסידורי של הרצף ההפוך, כך שהערך של 250 הופך ל-0000000052.

LoadData.java

String numberFormat = "%0" + maxLength + "d";

p.apply(GenerateSequence.from(0).to(max))
    .apply(
        ParDo.of(
            new DoFn<Long, Mutation>() {
              @ProcessElement
              public void processElement(@Element Long rowkey, OutputReceiver<Mutation> out) {
                String paddedRowkey = String.format(numberFormat, rowkey);

                // Reverse the rowkey for more efficient writing
                String reversedRowkey = new StringBuilder(paddedRowkey).reverse().toString();
                Put row = new Put(Bytes.toBytes(reversedRowkey));

                // Generate random bytes
                byte[] b = new byte[(int) rowSize];
                new Random().nextBytes(b);

                long timestamp = System.currentTimeMillis();
                row.addColumn(Bytes.toBytes(COLUMN_FAMILY), Bytes.toBytes("C"), timestamp, b);
                out.output(row);
              }
            }))
    .apply(CloudBigtableIO.writeToTable(bigtableTableConfig));

4. יצירת נתונים ב-Bigtable ומעקב אחרי הזרימה

הפקודות הבאות יריצו משימה של זרימת נתונים שיוצרת 40GB של נתונים לטבלה שלכם, יותר מהכמות מספיקה כדי להפעיל את Key Visualizer:

הפעלת Cloud Dataflow API

gcloud services enable dataflow.googleapis.com

קבלת הקוד מ-github ושינוי שלו בספרייה

git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
cd java-docs-samples/bigtable/beam/keyviz-art

יצירת הנתונים (הסקריפט נמשך כ-15 דקות)

mvn compile exec:java -Dexec.mainClass=keyviz.LoadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT"

מעקב אחר הייבוא

אפשר לעקוב אחרי המשימה בממשק המשתמש של Cloud Dataflow. אפשר גם לראות את העומס על המכונה של Cloud Bigtable באמצעות ממשק המשתמש למעקב.

בממשק המשתמש של Dataflow, תוכלו לראות את תרשים המשימות ואת מדדי המשימות השונים, כולל רכיבים שעובדו, יחידות vCPU נוכחיות ותפוקה.

9cecc290f5acea15.png

abb0561342dc6b60.png

ב-Bigtable יש כלי מעקב סטנדרטיים לפעולות קריאה וכתיבה, נפח אחסון בשימוש, שיעור שגיאות ועוד ברמת המכונה, האשכול והטבלה. בנוסף, ב-Bigtable יש גם את Key Visualizer, שמפרט את השימוש לפי מפתחות השורות שבהם נשתמש כשיצטברו נתונים של 30GB לפחות.

996f8589332dfc19.png

5. למידה: קריאה מ-Bigtable באמצעות Dataflow

מידע בסיסי על קריאה

כשקוראים מ-Cloud Bigtable, צריך לספק אובייקט של הגדרת CloudBigtableTableScanConfiguration. הפעולה הזו דומה לשורה CloudBigtableTableConfiguration, אבל אפשר להגדיר מהן השורות שצריך לסרוק ולקרוא.

Scan scan = new Scan();
scan.setCacheBlocks(false);
scan.setFilter(new FirstKeyOnlyFilter());

CloudBigtableScanConfiguration config =
    new CloudBigtableScanConfiguration.Builder()
        .withProjectId(options.getBigtableProjectId())
        .withInstanceId(options.getBigtableInstanceId())
        .withTableId(options.getBigtableTableId())
        .withScan(scan)
        .build();

לאחר מכן משתמשים בו כדי להתחיל את צינור עיבוד הנתונים:

p.apply(Read.from(CloudBigtableIO.read(config)))
    .apply(...

עם זאת, אם רוצים לבצע קריאה כחלק מצינור עיבוד הנתונים, אפשר להעביר CloudBigtableTableConfiguration ל-doFn שמרחיבים את AbstractCloudBigtableTableDoFn.

p.apply(GenerateSequence.from(0).to(10))
    .apply(ParDo.of(new ReadFromTableFn(bigtableTableConfig, options)));

לאחר מכן צריך לקרוא ל-super() עם ההגדרות האישיות ול-getConnection() כדי לקבל חיבור מבוזר.

public static class ReadFromTableFn extends AbstractCloudBigtableTableDoFn<Long, Void> {
    public ReadFromTableFn(CloudBigtableConfiguration config, ReadDataOptions readDataOptions) {
      super(config);
    }

    @ProcessElement
    public void processElement(PipelineOptions po) {
        Table table = getConnection().getTable(TableName.valueOf(options.getBigtableTableId()));
        ResultScanner imageData = table.getScanner(scan);
    }   
}

המשימה של ReadData Dataflow

ב-Codelab הזה צריך לקרוא מהטבלה כל שנייה, כדי להתחיל את צינור עיבוד הנתונים עם רצף שנוצר שמפעיל מספר טווחי קריאה לפי השעה שבה הוזן קובץ CSV.

יש קצת מתמטיקה כדי לקבוע אילו טווחי שורות לסרוק לפי הזמן, אבל אתם יכולים ללחוץ על שם הקובץ כדי להציג את קוד המקור אם אתם רוצים לקבל מידע נוסף.

ReadData.java

p.apply(GenerateSequence.from(0).withRate(1, new Duration(1000)))
    .apply(ParDo.of(new ReadFromTableFn(bigtableTableConfig, options)));

ReadData.java

  public static class ReadFromTableFn extends AbstractCloudBigtableTableDoFn<Long, Void> {

    List<List<Float>> imageData = new ArrayList<>();
    String[] keys;

    public ReadFromTableFn(CloudBigtableConfiguration config, ReadDataOptions readDataOptions) {
      super(config);
      keys = new String[Math.toIntExact(getNumRows(readDataOptions))];
      downloadImageData(readDataOptions.getFilePath());
      generateRowkeys(getNumRows(readDataOptions));
    }

    @ProcessElement
    public void processElement(PipelineOptions po) {
      // Determine which column will be drawn based on runtime of job.
      long timestampDiff = System.currentTimeMillis() - START_TIME;
      long minutes = (timestampDiff / 1000) / 60;
      int timeOffsetIndex = Math.toIntExact(minutes / KEY_VIZ_WINDOW_MINUTES);

      ReadDataOptions options = po.as(ReadDataOptions.class);
      long count = 0;

      List<RowRange> ranges = getRangesForTimeIndex(timeOffsetIndex, getNumRows(options));
      if (ranges.size() == 0) {
        return;
      }

      try {
        // Scan with a filter that will only return the first key from each row. This filter is used
        // to more efficiently perform row count operations.
        Filter rangeFilters = new MultiRowRangeFilter(ranges);
        FilterList firstKeyFilterWithRanges = new FilterList(
            rangeFilters,
            new FirstKeyOnlyFilter(),
            new KeyOnlyFilter());
        Scan scan =
            new Scan()
                .addFamily(Bytes.toBytes(COLUMN_FAMILY))
                .setFilter(firstKeyFilterWithRanges);

        Table table = getConnection().getTable(TableName.valueOf(options.getBigtableTableId()));
        ResultScanner imageData = table.getScanner(scan);
      } catch (Exception e) {
        System.out.println("Error reading.");
        e.printStackTrace();
      }
    }

    /**
     * Download the image data as a grid of weights and store them in a 2D array.
     */
    private void downloadImageData(String artUrl) {
    ...
    }

    /**
     * Generates an array with the rowkeys that were loaded into the specified Bigtable. This is
     * used to create the correct intervals for scanning equal sections of rowkeys. Since Bigtable
     * sorts keys lexicographically if we just used standard intervals, each section would have
     * different sizes.
     */
    private void generateRowkeys(long maxInput) {
    ...
    }

    /**
     * Get the ranges to scan for the given time index.
     */
    private List<RowRange> getRangesForTimeIndex(@Element Integer timeOffsetIndex, long maxInput) {
    ...
    }
  }

6. יצירת יצירת מופת

ad9c4c0b90626a3b.png

עכשיו, אחרי שהבנתם איך לטעון נתונים ב-Bigtable ולקרוא מהם באמצעות Dataflow, אתם יכולים להריץ את הפקודה הסופית שתיצור תמונה של המונה ליסה למשך יותר מ-8 שעות.

mvn compile exec:java -Dexec.mainClass=keyviz.ReadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT"

יש קטגוריה עם תמונות קיימות שאפשר להשתמש בה. באמצעות הכלי הזה תוכלו גם ליצור קובץ קלט מכל אחת מהתמונות שלכם, ואז להעלות אותם לקטגוריה ציבורית של GCS.

שמות הקבצים נוצרו מדוגמה אחת (gs://keyviz-art/[painting]_[hours]h.txt): gs://keyviz-art/american_gothic_4h.txt

אפשרויות ציור:

  • american_gothic
  • mona_lisa
  • pearl_earring
  • persistence_of_memory
  • starry_night
  • sunday_afternoon
  • the_scream

אפשרויות של שעות: 1, 4, 8, 12, 24, 48, 72, 96, 120, 144

כדי להגדיר את הקטגוריה או הקובץ ב-GCS כציבוריים, מקצים ל-allUsers את התפקיד Storage Object Viewer.

ee089815364150d2.png

לאחר בחירת התמונה, פשוט משנים את הפרמטר --file-path בפקודה הזו:

mvn compile exec:java -Dexec.mainClass=keyviz.ReadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT \
--filePath=gs://keyviz-art/american_gothic_4h.txt"

7. אבדוק זאת מאוחר יותר

עשויות לחלוף כמה שעות עד שהתמונה המלאה תתעורר לחיים, אבל לאחר 30 דקות אתם אמורים לראות פעילות בכלי הוויזואליזציה של המפתח. יש כמה פרמטרים שאפשר לשחק איתם: שינוי מרחק התצוגה, בהירות ומדד. אפשר לשנות את מרחק התצוגה, להשתמש בגלגל הגלילה שבעכבר או לגרור מלבן ברשת של כלי הוויזואליזציה של המקשים.

הבהירות משנה את קנה המידה של התמונה, וכך היא שימושית אם אתם רוצים לבחון לעומק אזור חם מאוד.

8e847f03df25572b.png

אפשר גם לקבוע איזה מדד יוצג. יש גם גרסאות OP – לקוח Read bytes, כותב לקוח בייטים, "קריאת לקוח בייטים" יוצר תמונות חלקות בזמן ש-"Ops" יוצרת תמונות עם יותר קווים, שיכולות להיראות ממש מגניבות בחלק מהתמונות.

33eb5dcf4e4be861.png

8. סיום

פינוי נפח אחסון כדי להימנע מחיובים

כדי להימנע מצבירת חיובים בחשבון Google Cloud Platform עבור המשאבים ששימשו ב-Codelab הזה, צריך למחוק את המכונה.

gcloud bigtable instances delete $INSTANCE_ID

אילו נושאים דיברנו?

  • כתיבה ב-Bigtable באמצעות Dataflow
  • קריאה מ-Bigtable באמצעות Dataflow (בתחילת צינור עיבוד הנתונים, באמצע צינור עיבוד הנתונים)
  • שימוש בכלי המעקב של Dataflow
  • שימוש בכלי המעקב של Bigtable, כולל Key Visualizer

השלבים הבאים