دیتابیسی که «درستی داده» را جدی میگیرد
PostgreSQL (به اختصار «پستگرس») ریشه در پروژهی POSTGRES دانشگاه برکلی در دههی ۱۹۸۰ دارد و از ۱۹۹۶ با نام فعلی بهصورت متنباز توسعه داده میشود. پشت آن یک شرکت واحد نیست؛ یک جامعهی جهانی و دهها شرکت با هم آن را جلو میبرند و مجوزش (PostgreSQL License، شبیه BSD) هر استفادهی تجاری را بدون هزینه و بدون الزام انتشار کد مجاز میکند. برای ما یک مزیت عملی دیگر هم دارد: هیچ بخشی از آن پشت لایسنس اشتراکی یا فعالسازی آنلاین نیست که با تحریم از کار بیفتد.
چه چیزی آن را متمایز میکند
- قیدها واقعاً اجرا میشوند: رشتهی بلندتر از ستون بیصدا بریده نمیشود، تاریخ نامعتبر به صفر تبدیل نمیشود و تبدیل نوع ضمنیِ خطرناک انجام نمیشود؛ خطا میگیرید.
- DDL تراکنشی: CREATE TABLE و ALTER TABLE را میتوان داخل یک تراکنش گذاشت؛ اگر مرحلهی سوم یک migration شکست بخورد، دو مرحلهی قبلی هم برمیگردند. در MySQL هر DDL یک commit ضمنی است.
- توسعهپذیری: نوع داده، تابع، عملگر و حتی نوع ایندکس جدید را میشود بهصورت اکستنشن اضافه کرد؛ PostGIS، pgvector و TimescaleDB همه اکستنشناند.
- SQL غنی: Window Function، CTE بازگشتی، LATERAL، jsonb با ایندکس GIN، انواع range، قید EXCLUDE و Full-Text Search داخلی.
- MVCC: خوانندهها نویسندهها را معطل نمیکنند و برعکس؛ بهای آن نیاز به VACUUM است که در فصل ۶ کامل میبینیم.
مقایسهی کوتاه با MySQL
| موضوع | PostgreSQL | MySQL (InnoDB) |
|---|---|---|
| DDL داخل تراکنش | بله، قابل ROLLBACK | خیر، commit ضمنی |
| مدل اتصال | یک پروسه برای هر اتصال؛ pooler تقریباً ضروری | یک thread برای هر اتصال |
| ایندکس جزئی (partial) | دارد | ندارد |
| JSON | jsonb باینری با ایندکس GIN روی کل سند | JSON با ایندکس روی ستونهای مجازی |
| انواع ویژه | array، range، uuid، inet، enum واقعی | محدودتر |
| نگهداری | VACUUM و autovacuum | purge داخلی InnoDB |
نسخهها و چرخهی انتشار
هر سال حدود مهرماه یک نسخهی اصلی منتشر میشود: 16 در ۲۰۲۳، 17 در ۲۰۲۴ و 18 در ۲۰۲۵. هر نسخهی اصلی پنج سال بهروزرسانی میگیرد و هر سه ماه یک نسخهی فرعی (مثل 17.6) میآید. از نسخهی 10 به بعد، عدد اول نسخهی اصلی است: رفتن از 17.2 به 17.6 فقط عوض کردن باینریها و یک restart است، اما رفتن از 16 به 17 ارتقای واقعی با pg_upgrade یا dump/restore لازم دارد (فصل ۷).
SELECT version();
SHOW server_version_num; -- 170006 یعنی 17.6
SELECT current_setting('server_version_num')::int >= 160000 AS is_16_or_newer;
نکتههایی که کمتر کسی میداند
- نامهای بدون کوتیشن به حروف کوچک تبدیل میشوند:
CREATE TABLE Ordersهمان orders است. اما اگر ابزاری جدول را با"Orders"بسازد، از آن به بعد همیشه باید با دابلکوتیشن صدایش کنید. از روز اول همهچیز را کوچک و snake_case بنویسید. - رشته با تککوتیشن است و شناسه با دابلکوتیشن؛
WHERE name = "ali"یعنی «ستونی به نام ali» و خطای column does not exist میدهد. این اولین دام کسانی است که از MySQL میآیند. - در release notes نسخههای فرعی گاهی نوشته میشود «پس از ارتقا این نوع ایندکسها را REINDEX کنید»؛ بخش Migration هر نسخهی فرعی را همیشه بخوانید.
- در اسکریپتها بهجای تجزیهی رشتهی version() از
server_version_numاستفاده کنید؛ یک عدد قابلمقایسه است. - حتی داخل یک تراکنش میتوانید
CREATE INDEXوDROP TABLEرا امتحان و بعد ROLLBACK کنید؛ روشی امن برای آزمودن اثر یک تغییر روی پلن کوئری (بهجز CREATE INDEX CONCURRENTLY که داخل تراکنش مجاز نیست).