Gcp
GcpIntermediate

GCP Data Analytics Services: Building Modern Data Platforms

6 min read
gcpdata-analyticsbigquerydataflowdata-processing

TL;DR

Master Google Cloud's data analytics services. Learn about BigQuery, Dataflow, Pub/Sub, Dataproc, and best practices for building scalable data analytics solutions.

GCP Data Analytics Services: Building Modern Data Platforms

Google Cloud Platform provides a comprehensive suite of services for building modern data analytics solutions. This guide covers key services and implementation best practices.

$1

``mermaid

graph TB

subgraph Analytics["Analytics Platform"]

direction TB

subgraph Ingestion["Data Ingestion"]

direction LR

PS["Pub/Sub"]

DF["Dataflow"]

DC["Data Connector"]

end

subgraph Processing["Data Processing"]

direction LR

BQ["BigQuery"]

DP["Dataproc"]

CF["Cloud Functions"]

end

subgraph Storage["Data Storage"]

direction LR

GCS["Cloud Storage"]

BT["Bigtable"]

DS["Datastore"]

end

end

subgraph Visualization["Data Visualization"]

direction TB

LS["Looker Studio"]

CH["Charts"]

DB["Dashboards"]

end

Analytics --> Visualization

classDef primary fill:#4285f4,stroke:#666,stroke-width:2px,color:#fff

classDef secondary fill:#34a853,stroke:#666,stroke-width:2px,color:#fff

classDef tertiary fill:#fbbc05,stroke:#666,stroke-width:2px,color:#fff

class Analytics,Ingestion primary

class Processing,Storage secondary

class Visualization tertiary

`

$1

$1

`sql

-- Create dataset

CREATE SCHEMA IF NOT EXISTS my_dataset

OPTIONS(

location="US",

default_partition_expiration_days=90,

description="Analytics dataset"

);

-- Create partitioned and clustered table

CREATE OR REPLACE TABLE my_dataset.events

(

event_id STRING,

user_id STRING,

event_timestamp TIMESTAMP,

event_type STRING,

device STRING,

location STRING,

properties JSON

)

PARTITION BY DATE(event_timestamp)

CLUSTER BY user_id, event_type

OPTIONS(

require_partition_filter=true,

partition_expiration_days=90,

description="Event tracking table"

);

`

$1

`sql

-- Efficient query with partition filter

WITH UserEvents AS (

SELECT

user_id,

event_type,

COUNT(*) as event_count,

MIN(event_timestamp) as first_event,

MAX(event_timestamp) as last_event

FROM my_dataset.events

WHERE DATE(event_timestamp) BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)

AND CURRENT_DATE()

GROUP BY user_id, event_type

)

SELECT

user_id,

ARRAY_AGG(STRUCT(

event_type,

event_count,

first_event,

last_event

)) as event_details,

SUM(event_count) as total_events

FROM UserEvents

GROUP BY user_id

HAVING total_events > 100

ORDER BY total_events DESC

LIMIT 1000;

`

$1

$1

`python

batch_pipeline.py

import apache_beam as beam

from apache_beam.options.pipeline_options import PipelineOptions

def parse_event(element):

"""Parse event data."""

import json

event = json.loads(element)

return {

'event_id': event['id'],

'user_id': event['user_id'],

'timestamp': event['timestamp'],

'event_type': event['type'],

'properties': event.get('properties', {})

}

def run_pipeline():

"""Execute batch processing pipeline."""

options = PipelineOptions()

with beam.Pipeline(options=options) as p:

events = (

p

| 'ReadFromGCS' >> beam.io.ReadFromText('gs://my-bucket/events/*.json')

| 'ParseJSON' >> beam.Map(parse_event)

| 'AddTimestamp' >> beam.Map(

lambda x: beam.window.TimestampedValue(x, x['timestamp']))

| 'WindowEvents' >> beam.WindowInto(

beam.window.FixedWindows(60 * 60)) # 1-hour windows

| 'CountByType' >> beam.CombinePerKey(sum)

| 'WriteToBigQuery' >> beam.io.WriteToBigQuery(

'project:dataset.table',

schema='event_type:STRING,count:INTEGER',

write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND,

create_disposition=beam.io.BigQueryDisposition.CREATE_NEVER)

)

`

$1

`python

streaming_pipeline.py

def run_streaming():

"""Execute streaming pipeline."""

options = PipelineOptions()

options.view_as(StandardOptions).streaming = True

with beam.Pipeline(options=options) as p:

events = (

p

| 'ReadFromPubSub' >> beam.io.ReadFromPubSub(

subscription='projects/my-project/subscriptions/my-subscription')

| 'ParseJSON' >> beam.Map(parse_event)

| 'AddEventTimestamp' >> beam.Map(

lambda x: beam.window.TimestampedValue(x, x['timestamp']))

| 'WindowEvents' >> beam.WindowInto(

beam.window.SlidingWindows(

size=60 * 60, # 1-hour windows

period=60)) # Sliding every minute

| 'ProcessEvents' >> beam.ParDo(ProcessEvents())

| 'WriteToBigQuery' >> beam.io.WriteToBigQuery(

'project:dataset.table',

schema='event_type:STRING,count:INTEGER,window_start:TIMESTAMP',

write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND,

create_disposition=beam.io.BigQueryDisposition.CREATE_NEVER)

)

`

$1

$1

`bash

Create topic

gcloud pubsub topics create my-topic

Create subscription

gcloud pubsub subscriptions create my-subscription \

--topic=my-topic \

--ack-deadline=30 \

--message-retention-duration=7d \

--expiration-period=never

`

$1

`python

publisher.py

from google.cloud import pubsub_v1

import json

import time

def publish_messages(project_id, topic_id):

"""Publish messages to Pub/Sub topic."""

publisher = pubsub_v1.PublisherClient()

topic_path = publisher.topic_path(project_id, topic_id)

def get_callback(future, data):

def callback(future):

try:

print(f"Published message ID: {future.result()}")

except Exception as e:

print(f"Error publishing {data}: {e}")

return callback

for i in range(100):

data = {

'message_id': i,

'timestamp': time.time(),

'data': f"Message {i}"

}

data_str = json.dumps(data)

future = publisher.publish(

topic_path,

data_str.encode('utf-8'),

origin="python-sample",

username="gcp-user"

)

future.add_done_callback(get_callback(future, data))

`

$1

$1

`yaml

cluster-config.yaml

config:

gceClusterConfig:

zoneUri: us-central1-a

subnetworkUri: projects/my-project/regions/us-central1/subnetworks/my-subnet

internalIpOnly: true

masterConfig:

numInstances: 1

machineTypeUri: n1-standard-4

diskConfig:

bootDiskSizeGb: 100

numLocalSsds: 1

workerConfig:

numInstances: 2

machineTypeUri: n1-standard-4

diskConfig:

bootDiskSizeGb: 100

numLocalSsds: 1

softwareConfig:

imageVersion: 2.0

optionalComponents:

- JUPYTER

- ZEPPELIN

`

$1

`python

spark_job.py

from pyspark.sql import SparkSession

from pyspark.sql.functions import *

def process_data():

"""Process data using Spark."""

spark = SparkSession.builder \

.appName("DataProcessing") \

.getOrCreate()

# Read data

df = spark.read.parquet("gs://my-bucket/data/*.parquet")

# Process data

result = df.groupBy("category") \

.agg(

count("*").alias("total_count"),

sum("amount").alias("total_amount"),

avg("amount").alias("avg_amount")

)

# Write results

result.write \

.mode("overwrite") \

.format("bigquery") \

.option("table", "project.dataset.results") \

.save()

`

$1

$1

`sql

-- Create view for dashboard

CREATE OR REPLACE VIEW my_dataset.dashboard_data AS

SELECT

DATE(event_timestamp) as event_date,

event_type,

device,

location,

COUNT(*) as event_count,

COUNT(DISTINCT user_id) as unique_users

FROM my_dataset.events

WHERE DATE(event_timestamp) >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)

GROUP BY event_date, event_type, device, location;

`

$1

`javascript

// visualization.js

const drawChart = (data) => {

const config = {

type: 'line',

data: {

labels: data.map(d => d.date),

datasets: [{

label: 'Event Count',

data: data.map(d => d.count),

borderColor: '#4285f4',

tension: 0.1

}]

},

options: {

responsive: true,

scales: {

y: {

beginAtZero: true

}

}

}

};

const ctx = document.getElementById('myChart').getContext('2d');

new Chart(ctx, config);

};

`

$1

$1

`sql

-- Optimize table partitioning

ALTER TABLE my_dataset.events

SET OPTIONS (

require_partition_filter=true,

partition_expiration_days=90

);

-- Create materialized view

CREATE MATERIALIZED VIEW my_dataset.daily_metrics

AS SELECT

DATE(event_timestamp) as event_date,

event_type,

COUNT(*) as event_count,

COUNT(DISTINCT user_id) as unique_users

FROM my_dataset.events

GROUP BY event_date, event_type;

`

$1

`python

pipeline_optimization.py

def optimize_pipeline(p):

"""Optimize Dataflow pipeline."""

return (p

| 'ReadData' >> beam.io.ReadFromSource(...)

| 'Reshuffle' >> beam.Reshuffle() # Optimize data distribution

| 'Transform' >> beam.ParDo(...)

.with_output_types(...) # Add type hints

| 'Combine' >> beam.CombinePerKey(

combine_fn=MyCombineFn(),

fanout=4) # Optimize combine operation

| 'Write' >> beam.io.WriteToBigQuery(...)

)

``

$1

1. BigQuery Optimization

- Use partitioning effectively

- Implement clustering

- Optimize query patterns

- Use materialized views

2. Pipeline Optimization

- Right-size workers

- Use appropriate machine types

- Optimize data flow

- Implement caching

3. Storage Optimization

- Use appropriate storage classes

- Implement lifecycle policies

- Compress data effectively

- Clean up temporary data

$1

1. Data Management

- Implement data governance

- Use proper schemas

- Regular data cleanup

- Version control

2. Security

- Implement proper IAM

- Encrypt sensitive data

- Monitor access patterns

- Regular security reviews

3. Performance

- Monitor resource usage

- Optimize queries

- Regular maintenance

- Scale appropriately

$1

GCP provides powerful services for building data analytics solutions. Key takeaways:

  • Choose appropriate services
  • Optimize performance
  • Implement security
  • Monitor costs
  • Follow best practices
  • For more information, refer to the official documentation:

  • [BigQuery Documentation](https://cloud.google.com/bigquery/docs)
  • [Dataflow Documentation](https://cloud.google.com/dataflow/docs)
  • [Pub/Sub Documentation](https://cloud.google.com/pubsub/docs)
  • [Dataproc Documentation](https://cloud.google.com/dataproc/docs)
  • Why This Matters

    Understanding the business and technical context helps you make informed decisions rather than blindly following patterns.

    Trade-offs to Consider

    Every architectural decision involves trade-offs. Consider your specific requirements, team expertise, and scale when evaluating options.

    When NOT to Use This

    Knowing when a solution doesn't apply is as valuable as knowing when it does. Consider alternatives for your specific situation.

    Decision Framework

    Use this framework to evaluate whether this approach is right for your use case based on your specific constraints and requirements.