How Short URLs Work: From Generation to High‑Performance Service Design

This article explains why short URLs are used in SMS and other platforms, outlines their benefits, describes the basic workflow of mapping long URLs to short ones, and dives into backend design choices such as incremental ID generation, storage strategies, caching, batch allocation, and distributed generation using Redis and Java.

ITFLY8 Architecture Home
ITFLY8 Architecture Home
ITFLY8 Architecture Home
How Short URLs Work: From Generation to High‑Performance Service Design

Most of us have received spam messages that contain short URLs, because short links fit the character limits of SMS and many social platforms.

example short URL
example short URL

Benefits of Short URLs

Character limits – SMS and micro‑blogging services have strict length constraints, so long links cannot be included in the message body.

Readability – Short links look cleaner and are easier for users to read.

Analytics – Clicks on short links can be recorded for statistical analysis.

Security – The original query parameters are hidden.

These reasons explain why most spam messages now contain short URLs.

Basic Short‑URL Workflow

The process consists of four steps:

A service maps a long URL to a short URL, e.g., www.baidu.com → www.t.cn/1.

The short URL is embedded in the SMS or other content and sent.

The user clicks the short URL; the browser follows a 301/302 redirect to the long URL.

The target content is displayed.

This article focuses on the first step – how to map a long URL to a short one.

Service Design

An ideal solution would use a deterministic algorithm that uniquely converts each long URL to a short one and also supports reverse lookup, but such a perfect compression algorithm does not exist.

The practical approach is to use an ID generator ("issuer"): each new long URL receives a sequential number, which is then encoded into a short string.

Example responses:

First URL → www.x.cn/0 Second URL →

www.x.cn/1

How to Store the Mapping?

The mapping must be persisted; otherwise it would be lost after a service restart. A relational database such as MySQL can store the long‑to‑short relationship, using an auto‑increment primary key when the traffic is low.

Ensuring One‑to‑One Mapping

With a simple incremental issuer, the same long URL submitted twice will receive different short URLs. Achieving strict one‑to‑one mapping requires additional storage and possibly an in‑memory cache, which increases space consumption.

A compromise is to store recent or hot mappings in a key‑value store (e.g., Redis) to reduce database load while providing faster lookups.

Short‑URL Storage Format

Short URLs are often generated by converting the numeric ID to a higher base (e.g., base‑32). Storing the numeric ID as a decimal integer is more space‑efficient and simplifies range queries; the conversion to a string occurs only when the short URL is returned.

High Concurrency

Directly incrementing a MySQL auto‑increment column for every request can become a bottleneck under high QPS. Common optimizations include:

Cache

Popular or recent long URLs can be cached in memory or Redis. If a request hits the cache, the short URL is returned without hitting the database.

Batch Issuing

Instead of requesting a single ID per request, the service can fetch a block of IDs (e.g., 10,000) from MySQL, allocate them in memory, and write back the used block asynchronously. This reduces the number of database round‑trips.

Distributed Generation

A single issuer is a single point of failure. One strategy is to run multiple issuers that allocate IDs with different step sizes (e.g., one issues even numbers, another odd numbers) or allocate distinct ranges (e.g., 0‑999, 1000‑1999, etc.). This allows many instances to operate independently without constant synchronization.

Implementation Example (Java + Redis)

package util;
import redis.clients.jedis.Jedis;

public class ShortURLUtil {
    private static final String SHORT_URL_KEY = "SHORT_URL_KEY";
    private static final String LOCALHOST = "http://localhost:4444/";
    private static final String SHORT_LONG_PREFIX = "short_long_prefix_";
    private static final String CACHE_KEY_PREFIX = "cache_key_prefix_";
    private static final int CACHE_SECONDS = 1 * 60 * 60;

    private final String redisConfig;
    private final Jedis jedis;

    public ShortURLUtil(String redisConfig) {
        this.redisConfig = redisConfig;
        this.jedis = new Jedis(this.redisConfig);
    }

    public String getShortURL(String longURL, Decimal decimal) {
        // Check cache
        String cache = jedis.get(CACHE_KEY_PREFIX + longURL);
        if (cache != null) {
            return LOCALHOST + toOtherBaseString(Long.valueOf(cache), decimal.x);
        }
        // Increment ID
        long num = jedis.incr(SHORT_URL_KEY);
        // Persist mapping (could be MySQL)
        jedis.set(SHORT_LONG_PREFIX + num, longURL);
        // Write to cache
        jedis.setex(CACHE_KEY_PREFIX + longURL, CACHE_SECONDS, String.valueOf(num));
        return LOCALHOST + toOtherBaseString(num, decimal.x);
    }

    private static final char[] digits = {
        '0','1','2','3','4','5','6','7','8','9',
        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'
    };

    private String toOtherBaseString(long n, int base) {
        long num = n < 0 ? ((long)2 * 0x7fffffff) + n + 2 : n;
        char[] buf = new char[32];
        int charPos = 32;
        while ((num / base) > 0) {
            buf[--charPos] = digits[(int)(num % base)];
            num /= base;
        }
        buf[--charPos] = digits[(int)(num % base)];
        return new String(buf, charPos, 32 - charPos);
    }

    enum Decimal { D32(32), D64(64); int x; Decimal(int x){ this.x = x; } }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(new ShortURLUtil("localhost").getShortURL("www.baidudu.com", Decimal.D32));
            System.out.println(new ShortURLUtil("localhost").getShortURL("www.baidu.com", Decimal.D64));
        }
    }
}

Source: https://juejin.im/post/6844903873950269454

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendjavaredishigh concurrencyurl-shorteningshort URL
ITFLY8 Architecture Home
Written by

ITFLY8 Architecture Home

ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.