シーケンスの代わりにuuidをIDとして使う

Heroku PostgreSQL 担当の人が id には uuid を使えと発表していたのでメモ。(時間がない人は、↓の動画の15分くらいからを見ましょう)

stop using numbers as IDs.
just use UUIDs. seriously
— Postgres: The Bits You Haven’t Found by pvh

UUID の違い

v1
Generate a UUID from a host ID, sequence number, and the current time.

v3
Generate a UUID from the MD5 hash of a namespace UUID and a name.

v4
Generate a random UUID

v5
Generate a UUID from the SHA-1 hash of a namespace UUID and a name.

この内、ID として利用できるのは v1 と v4 の2つ。v1 は最後 48 ビットがハード固有のノードを表し、uuid から生成時刻も復元できる。
各バージョンの違いは、この辺りの uuid ライブラリ実装者からもコメントも参考になる。

pros/cons

スライドには詳細が書かれていないので hacker news の投稿から掘り起こす。

uuid の一番のメリットは、universally unique な ID であること。(UUID そのままですが)

The biggest reason is that your IDs become universally unique – across shards, across database recoveries, rollbacks and session problems, you name it. These are all the kinds of things that can happen over the lifespan of a dataset.
https://news.ycombinator.com/item?id=5310637

これ以外にも

  • 外と通信せずに重複しない ID を採番できる
  • ID を推測できない。ID を外に公開するようなシステムの場合、特に重要。
  • うっかり別の ID を渡したり、別の ID で処理する確率がシーケンスの場合に比べて低い

などがある。
https://news.ycombinator.com/item?id=5311271

instagram ID との比較

Instagram が "timestamp + shard id + sequence" で ID を生成するようにした時、uuid も検討している。

Instagram にとっては ID が時系列順にソートされる必要があり、そのために "timestamp + uuid" で ID 生成することを検討していたが、データサイズが増えることもあり、見送っている。
また、Instagram 式では ID からどのシャードにあるのか簡単に特定できるので、運用面では助かっているそうだ。

ここからは、PostgreSQL で uuid を実際に使ってみる。

uuid モジュール(uuid-ossp)をインストール

スーパーユーザで実行する

# \dx
                 List of installed extensions
  Name   | Version |   Schema   |         Description
---------+---------+------------+------------------------------
 plpgsql | 1.0     | pg_catalog | PL/pgSQL procedural language
(1 row)

# CREATE EXTENSION "uuid-ossp";
CREATE EXTENSION
# \dx
                            List of installed extensions
   Name    | Version |   Schema   |                   Description
-----------+---------+------------+-------------------------------------------------
 plpgsql   | 1.0     | pg_catalog | PL/pgSQL procedural language
 uuid-ossp | 1.0     | public     | generate universally unique identifiers (UUIDs)
(2 rows)

PostgreSQL から uuid を生成

バージョン 1, 3, 4, 5 に対応している。

# select uuid_generate_v1();
           uuid_generate_v1
--------------------------------------
 e4ca39c4-8d9f-11e2-9f70-000c294a4696
(1 row)

# SELECT uuid_generate_v3(uuid_ns_url(), 'http://www.postgresql.org');
           uuid_generate_v3
--------------------------------------
 cf16fe52-3365-3a1f-8572-288d8d2aaa46
(1 row)

# SELECT uuid_generate_v4();
           uuid_generate_v4
--------------------------------------
 e630decd-ea28-4906-98ab-64e665183cc3
(1 row)

# SELECT uuid_generate_v5(uuid_ns_url(), 'http://www.postgresql.org');
           uuid_generate_v5
--------------------------------------
 e1ee1ad4-cd4e-5889-962a-4f605a68d94e
(1 row)

テーブルにデータを挿入

ID カラムのデフォルトを uuid v4 に設定し、データを挿入する。

CREATE TABLE t (
  uuid uuid PRIMARY KEY
            DEFAULT uuid_generate_v4(),
  name text);

# insert into t(name) values('a');
INSERT 0 1
# insert into t(name) values('b');
INSERT 0 1
# insert into t(name) values('c');
INSERT 0 1
# select * from t;
                 uuid                 | name
--------------------------------------+------
 ca130384-646d-459f-88e7-6ab9aa363070 | a
 1b03ade0-54f3-4875-bf4d-a4135bf66cbc | b
 ca6b4e72-e915-4359-8acc-3a0ea4348652 | c
(3 rows)

インデックスサイズを比較

100万データを serial と uuid それぞれで生成し、インデックスのサイズを比較。

CREATE TABLE t_uuid (
  uuid uuid PRIMARY KEY
            DEFAULT uuid_generate_v4(),
  num integer);
CREATE TABLE t_serial (
  id serial primary key,
  num integer);

# insert into t_uuid(num) select n from generate_series(1, 1000000) as n;
INSERT 0 1000000

# insert into t_serial(num) select n from generate_series(1, 1000000) as n;
INSERT 0 1000000

-- http://wiki.postgresql.org/wiki/Index_Maintenance#Index_size.2Fusage_statistics
SELECT
    t.tablename,
    indexname,
    c.reltuples AS num_rows,
    pg_size_pretty(pg_relation_size(quote_ident(t.tablename)::text)) AS table_size,
    pg_size_pretty(pg_relation_size(quote_ident(indexrelname)::text)) AS index_size,
    CASE WHEN x.is_unique = 1  THEN 'Y'
       ELSE 'N'
    END AS UNIQUE,
    idx_scan AS number_of_scans,
    idx_tup_read AS tuples_read,
    idx_tup_fetch AS tuples_fetched
FROM pg_tables t
LEFT OUTER JOIN pg_class c ON t.tablename=c.relname
LEFT OUTER JOIN
       (SELECT indrelid,
           max(CAST(indisunique AS integer)) AS is_unique
       FROM pg_index
       GROUP BY indrelid) x
       ON c.oid = x.indrelid
LEFT OUTER JOIN
    ( SELECT c.relname AS ctablename, ipg.relname AS indexname, x.indnatts AS number_of_columns, idx_scan, idx_tup_read, idx_tup_fetch,indexrelname FROM pg_index x
           JOIN pg_class c ON c.oid = x.indrelid
           JOIN pg_class ipg ON ipg.oid = x.indexrelid
           JOIN pg_stat_all_indexes psai ON x.indexrelid = psai.indexrelid )
    AS foo
    ON t.tablename = foo.ctablename
WHERE t.schemaname='public'
ORDER BY 1,2;

 tablename |   indexname   | num_rows | table_size | index_size | unique | number_of_scans | tuples_read | tuples_fetched
-----------+---------------+----------+------------+------------+--------+-----------------+-------------+----------------
 t_serial  | t_serial_pkey |    1e+06 | 35 MB      | 21 MB      | Y      |               0 |           0 |              0
 t_uuid    | t_uuid_pkey   |    1e+06 | 50 MB      | 39 MB      | Y      |               0 |           0 |              0
(2 rows)

uuid のほうが容量を約2倍消費している。

References

Advertisements
Tagged with: ,
Posted in database

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
  • RT @__apf__: How to write a research paper: a guide for software engineers & practitioners. docs.google.com/presentation/d… /cc @inwyrd 6 months ago
  • RT @HayatoChiba: 昔、自然と対話しながら数学に打ち込んだら何かを悟れるのではと思いたち、専門書1つだけ持ってパワースポットで名高い奈良の山奥に1週間籠ったことがある。しかし泊まった民宿にドカベンが全巻揃っていたため、水島新司と対話しただけで1週間過ぎた。 それ… 6 months ago
  • RT @googlecloud: Ever wonder what underwater fiber optic internet cables look like? Look no further than this deep dive w/ @NatAndLo: https… 6 months ago
  • @ijin UTC+01:00 な時間帯で生活しています、、、 1 year ago
  • RT @mattcutts: Google's world-class Site Reliability Engineering team wrote a new book: amazon.com/Site-Reliabili… It's about managing produc… 1 year ago
%d bloggers like this: