Tutorial: Data pecahan pada simpul pekerja di Azure Database for PostgreSQL – Hyperscale (Citus)

BERLAKU UNTUK: Azure Database for PostgreSQL - Hyperscale (Citus)

Dalam tutorial ini, Anda menggunakan Azure Database for PostgreSQL - Hyperscale (Citus) untuk mempelajari cara:

  • Membuat pecahan yang didistribusikan hash
  • Melihat tempat pecahan tabel ditempatkan
  • Mengidentifikasi distribusi miring
  • Membuat batasan pada tabel terdistribusi
  • Menjalankan kueri pada data terdistribusi

Prasyarat

Tutorial ini memerlukan grup server Hyperscale (Citus) yang sedang berjalan dengan dua simpul pekerja. Jika Anda tidak memiliki grup server yang berjalan, ikuti tutorial membuat grup server dan kemudian kembali ke yang satu ini.

Data yang didistribusikan hash

Mendistribusikan baris tabel di beberapa server PostgreSQL adalah teknik utama untuk kueri yang dapat diskalakan di Hyperscale (Citus). Bersama-sama, beberapa simpul dapat menyimpan lebih banyak data daripada database tradisional, dan dalam banyak kasus dapat menggunakan CPU pekerja secara paralel untuk menjalankan kueri.

Di bagian prasyarat, kami membuat grup server Hyperscale (Citus) dengan dua simpul pekerja.

coordinator and two workers

Tabel metadata simpul koordinator melacak pekerja dan mendistribusikan data. Kami dapat memeriksa pekerja aktif di tabel pg_dist_node.

select nodeid, nodename from pg_dist_node where isactive;
 nodeid | nodename
--------+-----------
      1 | 10.0.0.21
      2 | 10.0.0.23

Catatan

Nama simpul di Hyperscale (Citus) adalah alamat IP internal dalam jaringan virtual, dan alamat aktual yang Anda lihat mungkin berbeda.

Baris, pecahan, dan penempatan

Untuk menggunakan CPU dan sumber daya penyimpanan simpul pekerja, kami harus mendistribusikan data tabel di seluruh grup server. Mendistribusikan tabel yang menetapkan setiap baris ke grup logika yang disebut shard. Mari membuat tabel dan mendistribusikannya:

-- create a table on the coordinator
create table users ( email text primary key, bday date not null );

-- distribute it into shards on workers
select create_distributed_table('users', 'email');

Hyperscale (Citus) menetapkan setiap baris ke pecahan berdasarkan nilai kolom distribusi, yang, dalam kasus kami, kami tentukan menjadi email. Setiap baris akan berada di tepat satu pecahan, dan setiap pecahan dapat berisi beberapa baris.

users table with rows pointing to shards

Secara default create_distributed_table() membuat 32 pecahan, seperti yang dapat kami lihat dengan menghitung dalam tabel metadata pg_dist_shard:

select logicalrelid, count(shardid)
  from pg_dist_shard
 group by logicalrelid;
 logicalrelid | count
--------------+-------
 users        |    32

Hyperscale (Citus) menggunakan tabel pg_dist_shard untuk menetapkan baris ke pecahan, berdasarkan hash nilai di kolom distribusi. Detail hashing tidak penting untuk tutorial ini. Yang penting adalah bahwa kami dapat meminta untuk melihat peta nilai mana ke ID pecahan mana:

-- Where would a row containing hi@test.com be stored?
-- (The value doesn't have to actually be present in users, the mapping
-- is a mathematical operation consulting pg_dist_shard.)
select get_shard_id_for_distribution_column('users', 'hi@test.com');
 get_shard_id_for_distribution_column
--------------------------------------
                               102008

Pemetaan baris ke pecahan sangat logis. Pecahan harus ditetapkan ke simpul pekerja tertentu untuk penyimpanan, dalam apa yang Hyperscale (Citus) sebut penempatan pecahan.

shards assigned to workers

Kami dapat melihat penempatan pecahan di pg_dist_placement. Bergabung dengan tabel metadata lain yang pernah kami lihat menunjukkan tempat setiap pecahan berada.

-- limit the output to the first five placements

select
	shard.logicalrelid as table,
	placement.shardid as shard,
	node.nodename as host
from
	pg_dist_placement placement,
	pg_dist_node node,
	pg_dist_shard shard
where placement.groupid = node.groupid
  and shard.shardid = placement.shardid
order by shard
limit 5;
 table | shard  |    host
-------+--------+------------
 users | 102008 | 10.0.0.21
 users | 102009 | 10.0.0.23
 users | 102010 | 10.0.0.21
 users | 102011 | 10.0.0.23
 users | 102012 | 10.0.0.21

Penyimpangan data

Grup server berjalan paling efisien saat Anda menempatkan data secara merata pada simpul pekerja, dan saat Anda menempatkan data terkait bersama-sama pada pekerja yang sama. Di bagian ini kami akan berfokus pada bagian pertama, keseragaman penempatan.

Untuk menunjukkan, mari membuat sampel data untuk tabel users:

-- load sample data
insert into users
select
	md5(random()::text) || '@test.com',
	date_trunc('day', now() - random()*'100 years'::interval)
from generate_series(1, 1000);

Untuk melihat ukuran pecahan, kami dapat menjalankan fungsi ukuran tabel pada pecahan.

-- sizes of the first five shards
select *
from
	run_command_on_shards('users', $cmd$
	  select pg_size_pretty(pg_table_size('%1$s'));
	$cmd$)
order by shardid
limit 5;
 shardid | success | result
---------+---------+--------
  102008 | t       | 16 kB
  102009 | t       | 16 kB
  102010 | t       | 16 kB
  102011 | t       | 16 kB
  102012 | t       | 16 kB

Kami dapat melihat pecahan berukuran sama. Kami sudah melihat bahwa penempatan didistribusikan secara merata di antara pekerja, sehingga dapat menyimpulkan bahwa simpul pekerja menyimpan jumlah baris yang hampir sama.

Baris dalam contoh users didistribusikan secara merata karena properti kolom distribusi, email.

  1. Jumlah alamat email lebih besar dari atau sama dengan jumlah pecahan.
  2. Jumlah baris per alamat email serupa (dalam kasus kami, tepat satu baris per alamat karena kami menyatakan email kunci).

Setiap pilihan tabel dan kolom distribusi tempat salah satu properti gagal akan berakhir dengan ukuran data yang tidak merata pada pekerja, yaitu, penyimpangan data.

Menambahkan batasan ke data terdistribusi

Menggunakan Hyperscale (Citus) memungkinkan Anda terus menikmati keamanan database relasional, termasuk batasan database. Namun, ada batasannya. Karena sifat sistem terdistribusi, Hyperscale (Citus) tidak akan merujuk silang batasan keunikan atau integritas referensial antara simpul pekerja.

Mari mempertimbangkan contoh tabel users dengan tabel terkait.

-- books that users own
create table books (
	owner_email text references users (email),
	isbn text not null,
	title text not null
);

-- distribute it
select create_distributed_table('books', 'owner_email');

Untuk efisiensi, kami mendistribusikan books dengan cara yang sama seperti users: dengan alamat email pemilik. Mendistribusikan menurut nilai kolom serupa disebut kolokasi.

Kami tidak memiliki masalah mendistribusikan buku dengan kunci asing kepada pengguna, karena kuncinya ada di kolom distribusi. Namun, kami akan kesulitan membuat kunci untuk isbn:

-- will not work
alter table books add constraint books_isbn unique (isbn);
ERROR:  cannot create constraint on "books"
DETAIL: Distributed relations cannot have UNIQUE, EXCLUDE, or
        PRIMARY KEY constraints that do not include the partition column
        (with an equality operator if EXCLUDE).

Dalam tabel terdistribusi yang terbaik yang dapat kami lakukan adalah membuat kolom distribusi untuk kolom modulo unik:

-- a weaker constraint is allowed
alter table books add constraint books_isbn unique (owner_email, isbn);

Batasan di atas hanya membuat isbn unik per pengguna. Pilihan lain adalah membuat tabel referensi untuk buku daripada tabel terdistribusi, dan membuat tabel terdistribusi terpisah yang mengaitkan buku dengan pengguna.

Mengkueri tabel terdistribusi

Di bagian sebelumnya, kami melihat bagaimana baris tabel terdistribusi ditempatkan dalam pecahan pada simpul pekerja. Sebagian besar waktu Anda tidak perlu tahu bagaimana atau di mana data disimpan dalam grup server. Hyperscale (Citus) memiliki pelaksana kueri terdistribusi yang otomatis memisahkan kueri SQL reguler. Ini menjalankannya secara paralel pada simpul pekerja yang dekat dengan data.

Misalnya, kami dapat menjalankan kueri untuk menemukan usia rata-rata pengguna, memperlakukan tabel users terdistribusi seperti tabel normal pada koordinator.

select avg(current_date - bday) as avg_days_old from users;
    avg_days_old
--------------------
 17926.348000000000

query going to shards via coordinator

Di balik layar, pelaksana Hyperscale (Citus) membuat kueri terpisah untuk setiap pecahan, menjalankannya pada pekerja, dan menggabungkan hasilnya. Anda dapat melihatnya jika menggunakan perintah PostgreSQL EXPLAIN:

explain select avg(current_date - bday) from users;
                                  QUERY PLAN
----------------------------------------------------------------------------------
 Aggregate  (cost=500.00..500.02 rows=1 width=32)
   ->  Custom Scan (Citus Adaptive)  (cost=0.00..0.00 rows=100000 width=16)
     Task Count: 32
     Tasks Shown: One of 32
     ->  Task
       Node: host=10.0.0.21 port=5432 dbname=citus
       ->  Aggregate  (cost=41.75..41.76 rows=1 width=16)
         ->  Seq Scan on users_102040 users  (cost=0.00..22.70 rows=1270 width=4)

Output menunjukkan contoh rencana eksekusi untuk fragmen kueri yang berjalan pada pecahan 102040 (tabel users_102040 pada pekerja 10.0.0.21). Fragmen lain tidak ditampilkan karena mirip. Kami dapat melihat bahwa simpul pekerja memindai tabel pecahan dan menerapkan agregat. Simpul koordinator menggabungkan agregat untuk hasil akhir.

Langkah berikutnya

Dalam tutorial ini, kami membuat tabel terdistribusi, dan mempelajari tentang pecahan dan penempatannya. Kami melihat tantangan menggunakan keunikan dan batasan kunci asing, dan akhirnya melihat bagaimana kueri yang didistribusikan bekerja pada tingkat tinggi.