Kinesis and SQS are both AWS services for handling message queues, but Kinesis is fundamentally a real-time streaming service, while SQS is a traditional message queue service.

Let’s see Kinesis in action. Imagine we have a web application generating a constant stream of user activity events – clicks, page views, searches. We want to capture these events in real-time to analyze them, perhaps for fraud detection or to personalize user experiences.

Here’s a simplified producer sending data to a Kinesis Data Stream:

import boto3

kinesis_client = boto3.client('kinesis')
stream_name = 'my-user-activity-stream'

def send_event(user_id, event_type, timestamp):

    data = f'{{"userId": "{user_id}", "eventType": "{event_type}", "timestamp": {timestamp}}}'

    kinesis_client.put_record(
        StreamName=stream_name,
        Data=data.encode('utf-8'),
        PartitionKey=user_id # Ensures events for the same user go to the same shard
    )
    print(f"Sent event: {data}")

# Example usage
send_event('user-123', 'page_view', 1678886400)
send_event('user-456', 'click', 1678886401)
send_event('user-123', 'search', 1678886402)

And here’s a consumer reading from that stream:

import boto3
from pprint import pprint

kinesis_client = boto3.client('kinesis')
stream_name = 'my-user-activity-stream'

# Get stream description to find shards
response = kinesis_client.describe_stream(StreamName=stream_name)
shards = response['StreamDescription']['Shards']

# For simplicity, let's just process from the first shard
shard_id = shards[0]['ShardId']

# Get an iterator for the shard (e.g., TRIM_HORIZON starts from the oldest data)
response = kinesis_client.get_shard_iterator(
    StreamName=stream_name,
    ShardId=shard_id,
    ShardIteratorType='TRIM_HORIZON'
)
shard_iterator = response['ShardIterator']

# Loop to get records
while True:
    response = kinesis_client.get_records(
        ShardIterator=shard_iterator,
        Limit=10 # Process up to 10 records at a time
    )
    records = response['Records']
    if records:
        for record in records:
            data = record['Data'].decode('utf-8')
            print(f"Received: {data}")
        shard_iterator = response['NextShardIterator']
    else:
        print("No new records. Waiting...")
        # In a real application, you'd have more sophisticated waiting/polling
        import time
        time.sleep(5) # Wait a bit before checking again

This producer/consumer setup highlights Kinesis’s core functionality: continuous data flow. Data is written and read in a sequential, ordered manner within a shard.

The fundamental problem Kinesis solves is handling high-throughput, real-time data ingestion and processing. Think of clickstream data, IoT sensor readings, application logs, or financial market data. These are all sources generating data at a rate that traditional message queues struggle to keep up with efficiently, especially when order within a partition matters. Kinesis offers a durable, ordered, and scalable way to manage this firehose of information.

Internally, a Kinesis Data Stream is composed of one or more shards. Each shard is an independently scaled unit of throughput. When you create a stream, you define the number of shards. Data is written to a shard based on a PartitionKey. All records with the same PartitionKey are guaranteed to be written to the same shard, and thus processed in the order they were received within that shard. This is crucial for many use cases where maintaining order for a specific entity (like a user or a device) is critical. Producers PutRecord (or PutRecords for batching) to a stream, and consumers GetRecords from shards using a ShardIterator. The iterator keeps track of the consumer’s position within a shard. Kinesis Data Streams retain data for a configurable period, typically 24 hours to 7 days, allowing consumers to catch up if they fall behind.

The PartitionKey is your primary lever for controlling data distribution and ordering. If you have an event stream and want to ensure all events for a specific user are processed in order, you use the user ID as the PartitionKey. If you want to distribute the load evenly across shards and don’t have a strong ordering requirement per entity, you might use a random string or a hash of the data. The total throughput of your stream is the sum of the throughput of its shards. Each shard provides 1MB/s or 1000 records/s ingress and 2MB/s egress. You can scale your stream by adding or removing shards, which dynamically redistributes the data.

A common misconception is that Kinesis guarantees global ordering across all shards. This is not true. Ordering is only guaranteed within a shard. If your PartitionKey strategy leads to uneven distribution (hot shards), you can run into throughput bottlenecks and ordering issues for those specific keys.

When you’re dealing with a massive volume of data that needs to be processed in near real-time, and you care about the order of events for specific entities, Kinesis is your go-to. If you just need a simple, reliable way to decouple services and don’t require strict ordering or high-throughput streaming, SQS is often a better fit.

The next natural step after processing data from Kinesis is often to store it for longer-term analysis or to trigger further actions, which leads into services like Kinesis Data Firehose or Lambda.

Want structured learning?

Take the full Aws course →