Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1vBg1u-002nux-Vt for pgsql-performance@arkaria.postgresql.org; Wed, 22 Oct 2025 21:05:58 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1vBg1t-001HQS-VC for pgsql-performance@arkaria.postgresql.org; Wed, 22 Oct 2025 21:05:57 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1vBg1t-001HQA-Jy for pgsql-performance@lists.postgresql.org; Wed, 22 Oct 2025 21:05:56 +0000 Received: from mail-oo1-xc2d.google.com ([2607:f8b0:4864:20::c2d]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1vBg1q-003g6q-1P for pgsql-performance@lists.postgresql.org; Wed, 22 Oct 2025 21:05:56 +0000 Received: by mail-oo1-xc2d.google.com with SMTP id 006d021491bc7-6504c33afb1so33458eaf.1 for ; Wed, 22 Oct 2025 14:05:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1761167152; x=1761771952; darn=lists.postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=QNhLtISHpCO4sZfMC4Tk57261pWHUCvHMWRGslmmCL0=; b=lcVSaWlnGccbkg7fV8VivC1f/s/dCXtbWcTnF9ck2UTbObeKvVQOIgp6S+UZZ0shI5 k7eUE0h7e/RpNVurkEDwvJCVi3CmLWhgm5jfX3ODUTz9mo+k6b9Ncccb2Q7QAaf9zJmX 8EK2iDBbagu2ClYIWZ2qi5fHMEhHBr8xSfv5kY3Ub+2saTLFPOrOft6iCZGi1TOqJxY6 3d/Bd8hGvooQmgPN4Z9PF/53P9qiB6O9Fy/kQfzNBJOgQfOP7LdJS9sZwLjAnMs/T/6X NOQabwEnT30LVAEPwDum3QcHQb7mABwkMmc3rKGcN2aJeYTfbBcZbnBqbXcHbIpXONhB tAUA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1761167152; x=1761771952; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=QNhLtISHpCO4sZfMC4Tk57261pWHUCvHMWRGslmmCL0=; b=dVyb0oaNNRw0Ipk2TSGnliJWVrfjmyAhZ2zbWgNY51e+VVdxk5gPeW/P9o9rvUQ++E y0jeD3DrGn/3Z/rjI6muBJHh4mt+RwdLMrPL5tAX4+dezOS60yh9W+MZVxohF3Vcbk1h wSTmgArI17Ku02SZrVTlVIDPIusdEuT02LqZEKpx9kRM5F746mMIvRIjHdsx6qi/mdVI 1WOPHRkzbfwCdCJi2uligqh3WG5E8Q/qxXW7irMZ6qLmJYAhVG4l5qighG/xwu6YaOhy Gg91U1Dj/YXTtPHXkIlS2ArQaGfwT8jFL53WZL6StNO8UzYCRLELK9ejLGahehFWz3RF gXBg== X-Gm-Message-State: AOJu0YzT0eKpJjTpxuunu8hiBeZrrsthSHBWTEANO7s9dmzPj4sgqcf9 v3LiSmWFv21nb2uruiCVE2puH23iQ+bOWvpjXJMWeDsXdYTX7aJUnsqVhs2r32Wi0TOomLm3j/C EGaUr6QJYG64j+Dqq3Z9ULGg6RbbknXDBrPPI X-Gm-Gg: ASbGncsEeC3YnQRKgzjvsC2rN0GDbTswDdBxNqHUVQHcYhfHAEn9qfECDC6Z8VV2Jy5 XL23M7YuMszrN0/iYytry5SNRDYGsTbJ/wM0IIafDEyPJvAE6B+OoLQSk9Scezg63yh9SY3x1rk 803Q9TxYqinR7SFWM3DD9pDe9yseuG5l14D7Ku5TQwg0x/WvkE8xAQQx5jRJK/Om0C5UsU+vbkK ktPj8DGSd6kbUDGiaz2VBaFseuP1+lRQsVN+QUP2miMFqRlrJZS9WsW+62LN78gYietK8A= X-Google-Smtp-Source: AGHT+IHiKVYQF0qLdGyKeNnWV4rbqexj42xqHy9nFLWclF8m7Xv7ZXWJGSUT37QM7T5LWmoXEnB/D+121NSw4XZGiRc= X-Received: by 2002:a05:6870:c985:b0:3c1:d7af:de66 with SMTP id 586e51a60fabf-3cdb084746dmr1149624fac.7.1761167152340; Wed, 22 Oct 2025 14:05:52 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Olof Salberger Date: Wed, 22 Oct 2025 23:05:41 +0200 X-Gm-Features: AS18NWA8AzUFhLpQOFIOyYkQnDnjLmmDAdECfcGT1M3giJTcNaFkYiJzTAtIieo Message-ID: Subject: Re: Performance implications of partitioning by UUIDv7 range in PostgreSQL v18 To: Jonathan Reis Cc: pgsql-performance@lists.postgresql.org Content-Type: multipart/alternative; boundary="0000000000008164530641c5ae4b" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --0000000000008164530641c5ae4b Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable I don't know if it will necessarily be of much use in partition pruning, but it should work fairly well as a choice of clustered primary key together with block range indexes. On Wed, Oct 22, 2025 at 12:53=E2=80=AFPM Jonathan Reis wrote: > Hello PostgreSQL performance team, > > I=E2=80=99m evaluating the new UUIDv7 type in PostgreSQL v18 and would li= ke > advice on its suitability for time-based partitioning and related planner > behavior. > > *Context* > I have a large message/event table where each row is identified by a > uuidv7 primary key. Because UUIDv7 embeds a timestamp component in its > most significant bits, I=E2=80=99m considering using it as the partition = key > instead of a separate timestamptz column. > > *Questions* > > 1. > > *Partitioning on UUIDv7 ranges* > - > > Is range partitioning by UUIDv7 considered practical or advisable > for time-based data? > - > > Will the planner efficiently prune partitions when queries filter > by UUIDv7 ranges (e.g., WHERE id BETWEEN uuidv7_floor(timestamp1) > AND uuidv7_floor(timestamp2) that align with time periods? > - > > Are there known drawbacks=E2=80=94such as statistics accuracy, corr= elation > estimation, or index selectivity=E2=80=94when using UUIDv7 as a sur= rogate for > timestamptz? > 2. > > *Conversion between timestamptz and UUIDv7* > - > > Is there a built-in or community-recommended method to convert > between timestamptz and uuidv7 values? I am currently using this > > CREATE OR REPLACE FUNCTION uuidv7_floor(ts timestamptz) > RETURNS uuid > LANGUAGE sql > IMMUTABLE > AS $$ > WITH ms AS ( > SELECT floor(extract(epoch FROM ts) * 1000)::bigint AS ms > ), > h AS ( > SELECT lpad(to_hex(ms), 12, '0') AS h FROM ms > ) > SELECT ( > substr(h.h,1,8) || '-' || > substr(h.h,9,4) || '-' || > '7000' || '-' || -- version 7 + rand_a all zero > '8000' || '-' || -- variant '10' + rest zero > '000000000000' -- zero node > )::uuid > FROM h; > $$; > > *Example* > > CREATE TABLE message ( > id uuidv7 PRIMARY KEY, > payload jsonb, > received_at timestamptz DEFAULT now() > )PARTITION BY RANGE (id); > > I=E2=80=99d appreciate any insight into whether UUIDv7 is a good candidat= e for > partitioning from a performance standpoint, and how well partition prunin= g > behaves in practice. > > Best regards, > Jon > --0000000000008164530641c5ae4b Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I don't know if it will necessarily be of much use in = partition pruning, but it should work fairly well as a choice of clustered primary key=20 together with block range indexes.

On Wed, Oct 22, 202= 5 at 12:53=E2=80=AFPM Jonathan Reis <jon.reis@conevity.com> wrote:
Hello PostgreSQL performance team,

I=E2=80=99m evaluating the new UUIDv7 type in PostgreSQL v1= 8 and would like advice on its suitability for time-based partitioning and = related planner behavior.

Context
I have a large message/event table where each row is identified by a = uuidv7 primary key. Because UUIDv7 embeds a timestamp component in i= ts most significant bits, I=E2=80=99m considering using it as the partition= key instead of a separate timestamptz column.

Questions

  1. Partitioning on UUIDv7 ranges

    • Is range partitioning by UUIDv7 considered practical or advisable for ti= me-based data?

    • Will the planner efficiently prune partitions when queries filter by UUI= Dv7 ranges (e.g., WHERE id BETWEEN=C2=A0uuidv7_floor(tim= estamp1) AND=C2=A0uuidv7_floor(timestamp2)=C2=A0that al= ign with time periods?

    • Are there known drawbacks=E2=80=94such as statistics accuracy, correlati= on estimation, or index selectivity=E2=80=94when using UUIDv7 as a surrogat= e for timestamptz?

  2. Conversion between timestamptz and UUIDv7

    • Is there a built-in or community-recommended method to convert between <= code>timestamptz and uuidv7 values? I am currently usin= g this

      CREATE OR REPLACE FUNCTION uuidv7_floor(ts timestamptz)
      = =C2=A0 RETURNS uuid
      =C2=A0 LANGUAGE sql
      =C2=A0 IMMUTABLE
      AS $$
      = WITH ms AS (
      =C2=A0 SELECT floor(extract(epoch FROM ts) * 1000)::bigint = AS ms
      ),
      =C2=A0 =C2=A0 =C2=A0h AS (
      =C2=A0 =C2=A0 =C2=A0 =C2=A0SEL= ECT lpad(to_hex(ms), 12, '0') AS h FROM ms
      =C2=A0 =C2=A0 =C2=A0)=
      SELECT (
      =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0substr(h.h,1,8) || '-= ' ||
      =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0substr(h.h,9,4) || '-'= ; ||
      =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'7000' || '-' || = =C2=A0 =C2=A0 =C2=A0 =C2=A0 -- version 7 + rand_a all zero
      =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0'8000' || '-' || =C2=A0 =C2=A0 =C2=A0 = =C2=A0 -- variant '10' + rest zero
      =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0'000000000000' =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 -- zero nod= e
      =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0)::uuid
      FROM h;
      $$;

Example

CRE= ATE TABLE message ( id uuidv7 PRIMARY KEY, payload jsonb, received_at timestamptz DEFAULT now() ) PARTITION BY RANGE (id);

I=E2=80=99d appreciate any insight into whether UUIDv7 is a good candida= te for partitioning from a performance standpoint, and how well partition p= runing behaves in practice.

Best regards,
Jon

--0000000000008164530641c5ae4b--