PostgreSQL چیست؟ آموزش کامل SQL، JSONB و اتصال PostgreSQL به Python و FastAPI
PostgreSQL یک دیتابیس رابطهای قدرتمند برای ساخت Backend و سرویسهای هوش مصنوعی است. در این آموزش، نصب، SQL، طراحی Table، Constraint، Index، JSONB و ساخت تاریخچه چت با FastAPI و درواره را یاد میگیرید.
تقریباً هر نرمافزار کاربردی به فضایی برای نگهداری اطلاعات نیاز دارد. کاربران، سفارشها، پیامها، تنظیمات، تاریخچه گفتوگو، گزارش مصرف API و وضعیت پردازشها باید به شکلی پایدار و قابل جستوجو ذخیره شوند.
ذخیره این اطلاعات در فایل JSON یا متغیرهای داخل برنامه ممکن است برای یک نمونه اولیه کافی باشد، اما با افزایش تعداد کاربران و دادهها به مشکلاتی مانند جستوجوی دشوار، ناسازگاری اطلاعات، همزمانی و از دست رفتن داده میرسیم.
PostgreSQL یکی از شناختهشدهترین سیستمهای مدیریت پایگاه داده رابطهای و Object-relational است. این Database از SQL، Transaction، Constraint، Index، JSONB، Array، Full-text Search، Extension و قابلیتهای پیشرفته دیگری پشتیبانی میکند.
در این مقاله یاد میگیرید:
- PostgreSQL یا پستگرس چیست
- چه تفاوتی با MySQL، SQLite و Redis دارد
- Table، Row، Column و Schema چه هستند
- چگونه PostgreSQL را با Docker نصب کنیم
- چگونه Database و Table بسازیم
- عملیات CRUD را با SQL انجام دهیم
- Primary Key، Foreign Key و Constraint چه کاربردی دارند
- Index چگونه Query را سریعتر میکند
- Transaction و ACID چه هستند
- تفاوت JSON و JSONB چیست
- چگونه PostgreSQL را به Python و FastAPI متصل کنیم
- چگونه تاریخچه گفتوگوی هوش مصنوعی را در PostgreSQL ذخیره کنیم
- در محیط Production چه نکاتی را باید رعایت کنیم
PostgreSQL چیست؟
PostgreSQL یک سیستم مدیریت پایگاه داده رابطهای متنباز است. این نرمافزار دادهها را معمولاً در Tableهای دارای Column و Row نگهداری میکند و امکان تعریف رابطه میان Tableها را فراهم میسازد.
برای مثال، یک نرمافزار گفتوگوی هوش مصنوعی میتواند Tableهای زیر را داشته باشد:
users
conversations
messages
api_usage
هر Conversation به یک User تعلق دارد و هر Message به یک Conversation متصل است:
User
↓
Conversation
↓
Message
PostgreSQL فقط یک محل ذخیرهسازی ساده نیست. این Database میتواند موارد زیر را نیز مدیریت کند:
- Data Type
- Constraint
- Relationship
- Transaction
- Index
- View
- Materialized View
- Function
- Trigger
- Full-text Search
- JSONB
- Array
- Partitioning
- Replication
- Extension
PostgreSQL چگونه تلفظ میشود؟
در فارسی از شکلهای مختلفی استفاده میشود:
PostgreSQL
Postgres
پستگرس
پستگرسکیوال
خود پروژه استفاده از نام PostgreSQL و شکل کوتاه Postgres را رایج میداند. در این مقاله بیشتر از نام اصلی PostgreSQL استفاده میکنیم.
پایگاه داده رابطهای چیست؟
در Relational Database دادهها در Table ذخیره میشوند و رابطه میان آنها با Keyها تعریف میشود.
Table کاربران:
| id | name | |
|---|---|---|
| 1 | امیر | amir@example.com |
| 2 | سارا | sara@example.com |
Table گفتوگوها:
| id | user_id | title |
|---|---|---|
| 101 | 1 | آموزش PostgreSQL |
| 102 | 1 | طراحی API |
| 103 | 2 | خلاصهسازی متن |
فیلد user_id در Table گفتوگوها به id در Table کاربران اشاره میکند.
این رابطه اجازه میدهد تمام گفتوگوهای یک کاربر را پیدا کنیم:
SELECT
conversations.id,
conversations.title
FROM conversations
WHERE conversations.user_id = 1;
مفاهیم اصلی PostgreSQL
Database
مجموعهای مستقل از اطلاعات و Objectهای پایگاه داده است.
darvareh_app
Schema
Namespaceای داخل Database است که Tableها، Viewها و Objectهای دیگر را سازماندهی میکند.
Schema پیشفرض معمولاً:
public
نمونه Table در Schema مشخص:
app.users
billing.invoices
analytics.events
Table
ساختاری برای ذخیره دادههای مشابه است:
CREATE TABLE users (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);
Column
هر ویژگی داده را مشخص میکند:
id
name
email
created_at
Row
هر رکورد داخل Table یک Row است.
Primary Key
شناسه یکتای هر Row است:
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
Foreign Key
رابطه میان دو Table را تعریف میکند:
user_id BIGINT REFERENCES users(id)
Constraint
قواعد معتبر بودن داده را در سطح Database اعمال میکند.
PostgreSQL چه تفاوتی با SQL دارد؟
SQL یک زبان برای تعریف، خواندن و تغییر داده در Databaseهای رابطهای است.
PostgreSQL نرمافزاری است که SQL را اجرا میکند و امکانات Database را ارائه میدهد.
به بیان ساده:
SQL = زبان
PostgreSQL = سیستم مدیریت پایگاه داده
نرمافزارهای دیگری مانند MySQL، MariaDB، SQLite و SQL Server نیز از SQL استفاده میکنند، اما Syntax و قابلیتهای آنها کاملاً یکسان نیست.
PostgreSQL چه تفاوتی با MySQL، SQLite و Redis دارد؟
| ویژگی | PostgreSQL | MySQL | SQLite | Redis |
|---|---|---|---|---|
| نوع اصلی | Relational | Relational | Embedded Relational | In-memory Data Store |
| اجرای Server جداگانه | بله | بله | خیر | بله |
| SQL | بله | بله | بله | خیر |
| Transaction | پیشرفته | بله | بله | مدل متفاوت |
| JSON | بله، شامل JSONB | بله | محدودتر | با ساختارهای مختلف |
| مناسب Backend بزرگ | بله | بله | محدودتر | بهعنوان مکمل |
| مناسب Cache | ممکن، اما هدف اصلی نیست | ممکن | معمولاً خیر | بسیار مناسب |
| رابطه و Join | بسیار مناسب | مناسب | مناسب پروژه کوچک | هدف اصلی نیست |
| Extension | گسترده | محدودتر | محدود | قابلیتها متفاوت |
| استفاده معمول | داده اصلی برنامه | داده اصلی برنامه | Local و پروژه کوچک | Cache، Session و Queue |
هیچکدام در تمام سناریوها بهترین نیستند. در یک معماری رایج:
PostgreSQL → منبع اصلی داده
Redis → Cache و داده موقت
Object Storage → فایلها
API درواره → پردازش هوش مصنوعی
مهمترین مزایای PostgreSQL
پشتیبانی قدرتمند از SQL
PostgreSQL از Queryهای پیچیده، Join، Subquery، Window Function، CTE و Aggregate پشتیبانی میکند.
یکپارچگی داده
Constraintها اجازه نمیدهند هر دادهای بدون بررسی وارد Database شود.
Transaction
چند عملیات مرتبط میتوانند بهعنوان یک واحد اجرا شوند.
Data Typeهای متنوع
از جمله:
- Integer
- Numeric
- Text
- Boolean
- Date
- Timestamp
- UUID
- JSON و JSONB
- Array
- Range
- Network Address
- Geometric Type
JSONB
امکان نگهداری داده نیمهساختاریافته همراه با Query و Index را فراهم میکند.
Extension
قابلیتهای جدید میتوانند از طریق Extension اضافه شوند. برای مثال، pgvector برای نگهداری و جستوجوی Vectorها در پروژههای RAG استفاده میشود.
MVCC
PostgreSQL از Multi-Version Concurrency Control استفاده میکند تا Reader و Writerها در بسیاری از شرایط با تداخل کمتری همزمان کار کنند.
نصب PostgreSQL با Docker
برای محیط توسعه، Docker یکی از سادهترین روشهای اجرای PostgreSQL است.
فایل docker-compose.yml:
services:
postgres:
image: postgres:18-alpine
container_name: postgres-local
restart: unless-stopped
environment:
POSTGRES_DB: darvareh_app
POSTGRES_USER: app_user
POSTGRES_PASSWORD: change-this-local-password
ports:
- "127.0.0.1:5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test:
- CMD-SHELL
- pg_isready -U app_user -d darvareh_app
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
این تنظیم برای محیط Local است. در Production باید نسخه Image ثابت، Secretها، Backup، شبکه و منابع Server متناسب با زیرساخت تنظیم شوند.
اجرای PostgreSQL:
docker compose up -d
بررسی وضعیت:
docker compose ps
مشاهده Log:
docker compose logs postgres
اتصال با psql:
docker compose exec postgres \
psql -U app_user -d darvareh_app
اولین Commandهای psql
نمایش Databaseها:
\l
نمایش Tableها:
\dt
نمایش ساختار یک Table:
\d users
خروج:
\q
Commandهای دارای Backslash مربوط به ابزار psql هستند و SQL محسوب نمیشوند.
ساخت Table در PostgreSQL
فایل init.sql:
CREATE TABLE users (
id UUID PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE conversations (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id) ON DELETE SET NULL,
title TEXT NOT NULL,
model_id TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE messages (
id UUID PRIMARY KEY,
conversation_id UUID NOT NULL
REFERENCES conversations(id)
ON DELETE CASCADE,
role TEXT NOT NULL
CHECK (role IN ('system', 'user', 'assistant')),
content TEXT NOT NULL
CHECK (char_length(content) > 0),
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_conversations_user_created
ON conversations(user_id, created_at DESC);
CREATE INDEX idx_messages_conversation_created
ON messages(conversation_id, created_at ASC);
CREATE INDEX idx_conversations_metadata_gin
ON conversations USING GIN(metadata);
CREATE INDEX idx_messages_metadata_gin
ON messages USING GIN(metadata);
بررسی طراحی Tableها
UUID
در این پروژه شناسهها از نوع UUID هستند:
id UUID PRIMARY KEY
UUID در سیستمهای توزیعشده مفید است؛ زیرا برنامه میتواند شناسه را بدون مراجعه اولیه به Database تولید کند.
TIMESTAMPTZ
برای زمان از TIMESTAMPTZ استفاده شده است:
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
این نوع، Timestamp را با در نظر گرفتن Time Zone مدیریت میکند. معمولاً بهتر است زمانها در Backend بر اساس UTC ذخیره و هنگام نمایش به منطقه زمانی کاربر تبدیل شوند.
CHECK
مقادیر مجاز Role محدود شدهاند:
CHECK (
role IN ('system', 'user', 'assistant')
)
ON DELETE CASCADE
اگر Conversation حذف شود، Messageهای آن نیز حذف میشوند:
ON DELETE CASCADE
این رفتار باید آگاهانه انتخاب شود. در تمام رابطهها CASCADE بهترین گزینه نیست.
JSONB
Metadata انعطافپذیر در JSONB ذخیره میشود:
metadata JSONB NOT NULL DEFAULT '{}'::jsonb
Data Typeهای پرکاربرد PostgreSQL
| Data Type | کاربرد |
|---|---|
SMALLINT | عدد صحیح کوچک |
INTEGER | عدد صحیح معمول |
BIGINT | عدد صحیح بزرگ |
NUMERIC | عدد دقیق، مناسب مبالغ |
REAL | عدد اعشاری |
DOUBLE PRECISION | عدد اعشاری با دقت بیشتر |
TEXT | متن |
VARCHAR(n) | متن با سقف طول |
BOOLEAN | درست یا نادرست |
DATE | تاریخ |
TIME | زمان |
TIMESTAMP | تاریخ و زمان بدون Time Zone |
TIMESTAMPTZ | تاریخ و زمان با مدیریت Time Zone |
UUID | شناسه UUID |
JSON | JSON متنی |
JSONB | نمایش باینری و قابل Index از JSON |
BYTEA | داده باینری |
ARRAY | آرایه |
INET | آدرس شبکه |
برای مبلغ مالی، استفاده از FLOAT میتواند خطای اعشاری ایجاد کند. معمولاً NUMERIC یا نگهداری مبلغ در کوچکترین واحد صحیح انتخاب دقیقتری است.
Constraint چیست؟
Constraint قواعد داده را در Database اعمال میکند.
NOT NULL
فیلد نباید خالی باشد:
name TEXT NOT NULL
UNIQUE
مقدار نباید تکراری باشد:
email TEXT UNIQUE
PRIMARY KEY
شناسه یکتا و غیرخالی:
id UUID PRIMARY KEY
FOREIGN KEY
ارتباط با Table دیگر:
user_id UUID REFERENCES users(id)
CHECK
قانون سفارشی:
price NUMERIC CHECK (price >= 0)
DEFAULT
مقدار پیشفرض:
created_at TIMESTAMPTZ DEFAULT NOW()
Constraint فقط برای جلوگیری از خطای برنامه نیست. چند سرویس یا Script ممکن است مستقیماً به Database بنویسند؛ بنابراین قواعد مهم باید در لایه Database نیز اعمال شوند.
عملیات CRUD با SQL
CRUD مخفف Create، Read، Update و Delete است.
INSERT برای ایجاد داده
INSERT INTO users (
id,
name,
email
)
VALUES (
'5fbc4277-ae63-4b65-a1cf-7b3f2a673da9',
'امیر',
'amir@example.com'
);
دریافت Row ایجادشده:
INSERT INTO conversations (
id,
user_id,
title,
model_id
)
VALUES (
'8766a142-122b-4f57-b07a-8c8cfeab8241',
'5fbc4277-ae63-4b65-a1cf-7b3f2a673da9',
'آموزش PostgreSQL',
'YOUR_MODEL_ID'
)
RETURNING *;
SELECT برای خواندن داده
SELECT
id,
name,
email,
created_at
FROM users
ORDER BY created_at DESC;
دریافت یک کاربر:
SELECT *
FROM users
WHERE email = 'amir@example.com';
محدود کردن نتایج:
SELECT *
FROM conversations
ORDER BY created_at DESC
LIMIT 20;
UPDATE برای تغییر داده
UPDATE conversations
SET
title = 'راهنمای کامل PostgreSQL',
updated_at = NOW()
WHERE id = '8766a142-122b-4f57-b07a-8c8cfeab8241'
RETURNING *;
همیشه شرط WHERE را بررسی کنید. حذف WHERE میتواند تمام Rowهای Table را تغییر دهد.
DELETE برای حذف داده
DELETE FROM conversations
WHERE id = '8766a142-122b-4f57-b07a-8c8cfeab8241';
بدون WHERE تمام Rowها حذف میشوند:
DELETE FROM conversations;
در محیط Production پیش از اجرای Queryهای تغییردهنده، Scope آنها را دقیق بررسی کنید.
JOIN چیست؟
JOIN اطلاعات Tableهای مرتبط را ترکیب میکند.
نمایش Conversationها همراه نام کاربر:
SELECT
conversations.id,
conversations.title,
conversations.model_id,
users.name AS user_name
FROM conversations
LEFT JOIN users
ON users.id = conversations.user_id
ORDER BY conversations.created_at DESC;
INNER JOIN
فقط Rowهایی برگردانده میشوند که در هر دو طرف تطبیق دارند.
LEFT JOIN
تمام Rowهای سمت چپ برگردانده میشوند؛ حتی اگر در Table سمت راست تطبیقی وجود نداشته باشد.
RIGHT JOIN
تمام Rowهای سمت راست حفظ میشوند.
FULL JOIN
Rowهای هر دو طرف حفظ میشوند.
Aggregate Functionها
شمارش پیامها:
SELECT COUNT(*)
FROM messages;
تعداد پیام هر Conversation:
SELECT
conversation_id,
COUNT(*) AS message_count
FROM messages
GROUP BY conversation_id
ORDER BY message_count DESC;
تعداد Token ثبتشده در Metadata:
SELECT
SUM(
(metadata->>'total_tokens')::INTEGER
) AS total_tokens
FROM messages
WHERE metadata ? 'total_tokens';
CTE چیست؟
Common Table Expression یا CTE با WITH تعریف میشود و Query پیچیده را خواناتر میکند.
WITH conversation_stats AS (
SELECT
conversation_id,
COUNT(*) AS message_count
FROM messages
GROUP BY conversation_id
)
SELECT
conversations.title,
conversation_stats.message_count
FROM conversation_stats
JOIN conversations
ON conversations.id =
conversation_stats.conversation_id
ORDER BY conversation_stats.message_count DESC;
CTE همیشه به معنی سریعتر شدن Query نیست؛ بیشتر یک ابزار ساختاری است و Execution Plan باید بررسی شود.
Transaction چیست؟
Transaction مجموعهای از عملیات مرتبط است که بهعنوان یک واحد اجرا میشوند.
مثال انتقال اعتبار:
BEGIN;
UPDATE wallets
SET balance = balance - 1000
WHERE user_id = 'user_a';
UPDATE wallets
SET balance = balance + 1000
WHERE user_id = 'user_b';
COMMIT;
اگر عملیات دوم شکست بخورد:
ROLLBACK;
در این صورت تغییرات Transaction برگردانده میشوند.
ACID چیست؟
Atomicity
تمام عملیات Transaction انجام میشوند یا هیچکدام انجام نمیشوند.
Consistency
داده از یک وضعیت معتبر به وضعیت معتبر دیگری منتقل میشود.
Isolation
Transactionهای همزمان تا سطح تعریفشده از یکدیگر جدا هستند.
Durability
پس از Commit موفق، داده باید بر اساس تضمینهای تنظیمشده پایدار بماند.
سطحهای Isolation
PostgreSQL از سطحهای مختلف Isolation پشتیبانی میکند:
- Read Committed
- Repeatable Read
- Serializable
Read Committed معمولاً سطح پیشفرض است.
افزایش Isolation میتواند ناسازگاریهای بیشتری را کنترل کند، اما احتمال Retry شدن Transaction یا کاهش Concurrency را نیز افزایش میدهد.
بهترین Isolation Level به نوع عملیات بستگی دارد.
MVCC چیست؟
MVCC مخفف Multi-Version Concurrency Control است.
در این مدل، PostgreSQL نسخههایی از Rowها را مدیریت میکند تا بسیاری از Readerها و Writerها بدون قفلکردن کامل یکدیگر همزمان کار کنند.
مزایای عملی:
- SELECT معمولاً Write را مسدود نمیکند
- Transactionها Snapshot متناسب با Isolation Level میبینند
- Concurrency بهتر میشود
MVCC باعث ایجاد نسخههای قدیمی Row نیز میشود. PostgreSQL با Vacuum این نسخههای غیرضروری را مدیریت میکند.
VACUUM و Autovacuum
هنگام UPDATE یا DELETE، نسخههای قدیمی Row ممکن است فوراً از فایل فیزیکی حذف نشوند.
Vacuum برای موارد زیر اهمیت دارد:
- بازیابی فضای قابل استفاده
- مدیریت Dead Tupleها
- حفظ عملکرد Query
- بهروزرسانی اطلاعات موردنیاز Planner
- جلوگیری از مشکلات مربوط به Transaction ID
در بیشتر پروژهها Autovacuum باید فعال باقی بماند. خاموش کردن آن بدون دلیل و مانیتورینگ میتواند به افت عملکرد منجر شود.
اجرای دستی تحلیل آماری:
ANALYZE messages;
Vacuum همراه تحلیل:
VACUUM ANALYZE messages;
اجرای VACUUM FULL رفتار و Lock متفاوتی دارد و نباید بدون بررسی روی Table پرترافیک اجرا شود.
Index چیست؟
Index ساختاری است که میتواند پیدا کردن Rowها را سریعتر کند.
بدون Index، PostgreSQL ممکن است تمام Table را بررسی کند:
Sequential Scan
با Index مناسب، فقط بخش لازم بررسی میشود:
Index Scan
نمونه:
CREATE INDEX idx_messages_conversation_created
ON messages(conversation_id, created_at DESC);
Query مرتبط:
SELECT *
FROM messages
WHERE conversation_id = $1
ORDER BY created_at DESC
LIMIT 20;
آیا Index همیشه مفید است؟
خیر. Index هزینه دارد:
- فضای Disk مصرف میکند
- INSERT را سنگینتر میکند
- UPDATE و DELETE باید Index را نیز تغییر دهند
- Index بدون استفاده نیازمند نگهداری است
برای هر Column بهصورت خودکار Index نسازید. Index باید بر اساس Queryهای واقعی طراحی شود.
ترتیب Columnها در Index ترکیبی
Index:
CREATE INDEX idx_messages_conversation_created
ON messages(conversation_id, created_at DESC);
برای Queryهایی مناسب است که ابتدا بر اساس conversation_id فیلتر و سپس بر اساس created_at مرتب میشوند.
این Index الزاماً برای Query زیر به همان اندازه مفید نیست:
SELECT *
FROM messages
ORDER BY created_at DESC;
ترتیب Columnهای Index به الگوی Query وابسته است.
انواع Index در PostgreSQL
B-tree
رایجترین Index برای:
- برابری
- مقایسه
- Range
- Sort
GIN
برای مقادیر دارای چند جزء مناسب است:
- JSONB
- Array
- Full-text Search
GiST
برای برخی دادههای هندسی، Range، Search و Extensionها استفاده میشود.
BRIN
برای Tableهای بسیار بزرگ که دادهها از نظر فیزیکی با مقدار Column همبستگی دارند، مانند Timestampهای افزایشی، میتواند کمحجم باشد.
Hash
برای برخی مقایسههای برابری کاربرد دارد، اما B-tree در بسیاری از سناریوهای عمومی انتخاب رایجتری است.
EXPLAIN و EXPLAIN ANALYZE
برای مشاهده Execution Plan:
EXPLAIN
SELECT *
FROM messages
WHERE conversation_id =
'8766a142-122b-4f57-b07a-8c8cfeab8241';
برای اجرای واقعی و مشاهده زمان:
EXPLAIN ANALYZE
SELECT *
FROM messages
WHERE conversation_id =
'8766a142-122b-4f57-b07a-8c8cfeab8241';
EXPLAIN ANALYZE واقعاً Query را اجرا میکند. اگر Query شامل UPDATE، DELETE یا INSERT باشد، میتواند داده را تغییر دهد. برای بررسی Queryهای تغییردهنده باید از Transaction کنترلشده استفاده کنید.
نسخه کاملتر برای مشاهده Bufferها:
EXPLAIN (
ANALYZE,
BUFFERS,
FORMAT TEXT
)
SELECT *
FROM messages
WHERE conversation_id = $1;
JSON و JSONB چه تفاوتی دارند؟
PostgreSQL دو Data Type اصلی برای JSON دارد:
JSON
JSONB
JSON
متن JSON را تقریباً با شکل ورودی نگه میدارد.
JSONB
JSON را در ساختار باینری قابل پردازش ذخیره میکند.
در بیشتر کاربردهایی که نیاز به Query، Operator و Index دارید، JSONB انتخاب رایجتری است.
| ویژگی | JSON | JSONB |
|---|---|---|
| حفظ دقیق Format ورودی | بیشتر | خیر |
| پردازش Query | محدودتر | مناسبتر |
| Index با GIN | محدود | بله |
| سرعت درج اولیه | ممکن است کمتر پردازش شود | نیازمند تبدیل |
| جستوجوی داخلی | محدودتر | قدرتمندتر |
| استفاده معمول | نگهداری متن اصلی JSON | Metadata و داده قابل Query |
درج داده JSONB
INSERT INTO conversations (
id,
title,
model_id,
metadata
)
VALUES (
'8766a142-122b-4f57-b07a-8c8cfeab8241',
'گفتوگوی آزمایشی',
'YOUR_MODEL_ID',
'{
"language": "fa",
"source": "web",
"tags": ["postgresql", "ai"]
}'::jsonb
);
Query روی JSONB
دریافت مقدار یک Field بهصورت JSON:
SELECT metadata->'language'
FROM conversations;
دریافت آن بهصورت Text:
SELECT metadata->>'language'
FROM conversations;
فیلتر بر اساس Field:
SELECT *
FROM conversations
WHERE metadata->>'language' = 'fa';
بررسی وجود Key:
SELECT *
FROM conversations
WHERE metadata ? 'source';
Containment:
SELECT *
FROM conversations
WHERE metadata @> '{"language": "fa"}'::jsonb;
ساخت GIN Index برای JSONB
CREATE INDEX idx_conversations_metadata_gin
ON conversations
USING GIN(metadata);
این Index میتواند بعضی Queryهای JSONB، مانند Containment، را سریعتر کند.
هر Query دارای metadata->> الزاماً از Index عمومی GIN به بهترین شکل استفاده نمیکند. برای یک مسیر پرتکرار میتوان Expression Index ساخت:
CREATE INDEX idx_conversations_language
ON conversations (
(metadata->>'language')
);
طراحی Index باید با Query واقعی و Execution Plan هماهنگ باشد.
چه چیزی را در JSONB ذخیره کنیم؟
JSONB برای دادههای انعطافپذیر مانند این موارد مناسب است:
- Metadata
- تنظیمات متغیر
- پارامترهای مدل
- اطلاعات Provider
- Usage جزئی
- ویژگیهای اختیاری
- Snapshot پاسخ بیرونی
دادههای اصلی و پرتکرار بهتر است Column مشخص داشته باشند:
نامناسب:
{
"id": "...",
"user_id": "...",
"created_at": "...",
"status": "active"
}
اگر دائماً روی user_id، created_at و status Query میزنید، بهتر است Column مستقل باشند.
الگوی مناسب:
Columnهای مشخص → داده اصلی و پرتکرار
JSONB → Metadata انعطافپذیر
Pagination در PostgreSQL
Offset Pagination
SELECT *
FROM messages
ORDER BY created_at DESC
LIMIT 20 OFFSET 40;
مزایا:
- ساده
- امکان رفتن مستقیم به صفحه
معایب:
- Offset بزرگ میتواند کند شود
- با درج داده جدید احتمال جابهجایی نتایج وجود دارد
Keyset Pagination
SELECT *
FROM messages
WHERE (
created_at,
id
) < (
$1,
$2
)
ORDER BY created_at DESC, id DESC
LIMIT 20;
مزایا:
- مناسبتر برای Dataset بزرگ
- رفتار پایدارتر در Feed
معایب:
- رفتن مستقیم به صفحه دلخواه دشوارتر است
- نیازمند Cursor و Sort پایدار است
Connection Pool چیست؟
باز کردن Connection جدید برای هر Request هزینه دارد. Connection Pool مجموعهای از Connectionهای آماده را نگه میدارد.
Request
↓
دریافت Connection از Pool
↓
اجرای Query
↓
بازگرداندن Connection به Pool
مزایا:
- کاهش هزینه ایجاد Connection
- کنترل تعداد Connectionهای همزمان
- کاهش فشار روی PostgreSQL
- مدیریت بهتر منابع
Pool بسیار بزرگ نیز مناسب نیست. اگر هر Instance برنامه ۵۰ Connection و ۲۰ Instance داشته باشد، تعداد نظری Connectionها به ۱۰۰۰ میرسد.
اندازه Pool باید بر اساس موارد زیر تعیین شود:
- تعداد Workerها
- تعداد Instanceها
- ظرفیت PostgreSQL
- مدت Queryها
- Concurrency
- وجود Pooler بیرونی
- Workload واقعی
اتصال Python به PostgreSQL با Psycopg
Psycopg یکی از Adapterهای شناختهشده PostgreSQL برای Python است.
نصب:
pip install "psycopg[binary,pool]"
نمونه ساده:
import psycopg
with psycopg.connect(
"postgresql://app_user:password@localhost:5432/darvareh_app"
) as connection:
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT id, name, email
FROM users
WHERE email = %s
""",
("amir@example.com",),
)
row = cursor.fetchone()
print(row)
از Parameter Binding استفاده کنید و مقدار کاربر را با String Formatting وارد SQL نکنید.
نامناسب:
query = (
"SELECT * FROM users "
f"WHERE email = '{user_email}'"
)
مناسب:
cursor.execute(
"SELECT * FROM users WHERE email = %s",
(user_email,),
)
پروژه عملی: ذخیره تاریخچه چت در PostgreSQL
در این پروژه:
- Conversation جدید ساخته میشود.
- پیام User در PostgreSQL ذخیره میشود.
- تاریخچه اخیر Conversation خوانده میشود.
- پیامها به API درواره ارسال میشوند.
- پاسخ مدل در PostgreSQL ذخیره میشود.
- پاسخ نهایی به Client برمیگردد.
ساختار پروژه
postgres-ai-chat/
├── app.py
├── init.sql
├── requirements.txt
├── docker-compose.yml
└── .env
نصب وابستگیها
فایل requirements.txt:
fastapi
uvicorn[standard]
httpx
psycopg[binary,pool]
python-dotenv
نصب:
pip install -r requirements.txt
تنظیم Environment Variableها
فایل .env:
DATABASE_URL=postgresql://app_user:change-this-local-password@localhost:5432/darvareh_app
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
فایل .gitignore:
.env
.venv/
__pycache__/
کد کامل FastAPI
فایل app.py:
import os
from contextlib import asynccontextmanager
from uuid import UUID, uuid4
import httpx
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from psycopg.rows import dict_row
from psycopg.types.json import Jsonb
from psycopg_pool import AsyncConnectionPool
from pydantic import BaseModel, Field
load_dotenv()
DATABASE_URL = os.getenv("DATABASE_URL")
DARVAREH_API_KEY = os.getenv("DARVAREH_API_KEY")
DARVAREH_MODEL_ID = os.getenv("DARVAREH_MODEL_ID")
DARVAREH_URL = (
"https://api.darvareh.ir/v1/chat/completions"
)
if not DATABASE_URL:
raise RuntimeError(
"متغیر DATABASE_URL تنظیم نشده است."
)
if not DARVAREH_API_KEY:
raise RuntimeError(
"متغیر DARVAREH_API_KEY تنظیم نشده است."
)
if not DARVAREH_MODEL_ID:
raise RuntimeError(
"متغیر DARVAREH_MODEL_ID تنظیم نشده است."
)
class CreateConversationRequest(BaseModel):
title: str = Field(
min_length=1,
max_length=200,
)
class ConversationResponse(BaseModel):
id: UUID
title: str
model_id: str
class CreateMessageRequest(BaseModel):
content: str = Field(
min_length=1,
max_length=20_000,
)
class ChatResponse(BaseModel):
conversation_id: UUID
user_message_id: UUID
assistant_message_id: UUID
content: str
model: str
@asynccontextmanager
async def lifespan(app: FastAPI):
pool = AsyncConnectionPool(
conninfo=DATABASE_URL,
min_size=1,
max_size=10,
open=False,
kwargs={
"row_factory": dict_row,
},
)
await pool.open()
await pool.wait()
app.state.db_pool = pool
yield
await pool.close()
app = FastAPI(
title="PostgreSQL AI Chat API",
version="1.0.0",
lifespan=lifespan,
)
@app.get("/health")
async def health_check():
try:
async with app.state.db_pool.connection() as conn:
cursor = await conn.execute(
"SELECT 1 AS value"
)
row = await cursor.fetchone()
return {
"status": "ok",
"database": (
"ok" if row["value"] == 1
else "unavailable"
),
}
except Exception:
return {
"status": "degraded",
"database": "unavailable",
}
@app.post(
"/v1/conversations",
response_model=ConversationResponse,
status_code=201,
)
async def create_conversation(
payload: CreateConversationRequest,
):
conversation_id = uuid4()
async with app.state.db_pool.connection() as conn:
cursor = await conn.execute(
"""
INSERT INTO conversations (
id,
title,
model_id,
metadata
)
VALUES (
%s,
%s,
%s,
%s
)
RETURNING
id,
title,
model_id
""",
(
conversation_id,
payload.title,
DARVAREH_MODEL_ID,
Jsonb({
"language": "fa",
"source": "api",
}),
),
)
row = await cursor.fetchone()
return ConversationResponse(**row)
@app.post(
"/v1/conversations/{conversation_id}/messages",
response_model=ChatResponse,
)
async def create_message(
conversation_id: UUID,
payload: CreateMessageRequest,
):
user_message_id = uuid4()
async with app.state.db_pool.connection() as conn:
cursor = await conn.execute(
"""
SELECT id
FROM conversations
WHERE id = %s
""",
(conversation_id,),
)
conversation = await cursor.fetchone()
if not conversation:
raise HTTPException(
status_code=404,
detail={
"code": "CONVERSATION_NOT_FOUND",
"message": (
"گفتوگوی موردنظر پیدا نشد."
),
},
)
await conn.execute(
"""
INSERT INTO messages (
id,
conversation_id,
role,
content
)
VALUES (%s, %s, 'user', %s)
""",
(
user_message_id,
conversation_id,
payload.content,
),
)
async with app.state.db_pool.connection() as conn:
cursor = await conn.execute(
"""
SELECT role, content
FROM (
SELECT
role,
content,
created_at,
id
FROM messages
WHERE conversation_id = %s
ORDER BY created_at DESC, id DESC
LIMIT 20
) AS recent_messages
ORDER BY created_at ASC, id ASC
""",
(conversation_id,),
)
history = await cursor.fetchall()
messages = [
{
"role": "system",
"content": (
"پاسخها را دقیق، روشن و "
"به زبان فارسی ارائه کن."
),
}
]
messages.extend(
{
"role": row["role"],
"content": row["content"],
}
for row in history
)
request_body = {
"model": DARVAREH_MODEL_ID,
"messages": messages,
}
headers = {
"Authorization": (
f"Bearer {DARVAREH_API_KEY}"
),
"Content-Type": "application/json",
}
timeout = httpx.Timeout(
connect=10.0,
read=60.0,
write=20.0,
pool=10.0,
)
try:
async with httpx.AsyncClient(
timeout=timeout
) as client:
response = await client.post(
DARVAREH_URL,
headers=headers,
json=request_body,
)
if response.status_code == 429:
raise HTTPException(
status_code=503,
detail={
"code": "UPSTREAM_RATE_LIMIT",
"message": (
"سرویس هوش مصنوعی موقتاً "
"با محدودیت درخواست مواجه است."
),
},
)
if response.status_code >= 500:
raise HTTPException(
status_code=502,
detail={
"code": "UPSTREAM_ERROR",
"message": (
"سرویس هوش مصنوعی پاسخ "
"معتبری برنگرداند."
),
},
)
if response.status_code >= 400:
raise HTTPException(
status_code=502,
detail={
"code": "UPSTREAM_REJECTED",
"message": (
"درخواست توسط سرویس "
"بالادستی پذیرفته نشد."
),
},
)
body = response.json()
assistant_content = (
body["choices"][0]["message"]["content"]
.strip()
)
usage = body.get("usage", {})
except httpx.TimeoutException as exc:
raise HTTPException(
status_code=504,
detail={
"code": "UPSTREAM_TIMEOUT",
"message": (
"سرویس هوش مصنوعی در زمان "
"تعیینشده پاسخ نداد."
),
},
) from exc
except httpx.RequestError as exc:
raise HTTPException(
status_code=502,
detail={
"code": "UPSTREAM_CONNECTION_ERROR",
"message": (
"ارتباط با سرویس "
"هوش مصنوعی برقرار نشد."
),
},
) from exc
except (
ValueError,
KeyError,
IndexError,
TypeError,
) as exc:
raise HTTPException(
status_code=502,
detail={
"code": "INVALID_UPSTREAM_RESPONSE",
"message": (
"ساختار پاسخ سرویس "
"هوش مصنوعی معتبر نبود."
),
},
) from exc
if not assistant_content:
raise HTTPException(
status_code=502,
detail={
"code": "EMPTY_UPSTREAM_RESPONSE",
"message": (
"سرویس هوش مصنوعی "
"پاسخ خالی برگرداند."
),
},
)
assistant_message_id = uuid4()
async with app.state.db_pool.connection() as conn:
await conn.execute(
"""
INSERT INTO messages (
id,
conversation_id,
role,
content,
metadata
)
VALUES (
%s,
%s,
'assistant',
%s,
%s
)
""",
(
assistant_message_id,
conversation_id,
assistant_content,
Jsonb({
"model": DARVAREH_MODEL_ID,
"usage": usage,
}),
),
)
await conn.execute(
"""
UPDATE conversations
SET updated_at = NOW()
WHERE id = %s
""",
(conversation_id,),
)
return ChatResponse(
conversation_id=conversation_id,
user_message_id=user_message_id,
assistant_message_id=assistant_message_id,
content=assistant_content,
model=DARVAREH_MODEL_ID,
)
@app.get(
"/v1/conversations/{conversation_id}/messages"
)
async def list_messages(
conversation_id: UUID,
limit: int = 50,
):
safe_limit = min(max(limit, 1), 100)
async with app.state.db_pool.connection() as conn:
cursor = await conn.execute(
"""
SELECT
id,
role,
content,
metadata,
created_at
FROM messages
WHERE conversation_id = %s
ORDER BY created_at ASC, id ASC
LIMIT %s
""",
(
conversation_id,
safe_limit,
),
)
rows = await cursor.fetchall()
return {
"data": rows,
"count": len(rows),
}
اجرای پروژه
ابتدا PostgreSQL را اجرا کنید:
docker compose up -d
سپس API را اجرا کنید:
uvicorn app:app --reload
آدرس API:
http://127.0.0.1:8000
مستندات FastAPI:
http://127.0.0.1:8000/docs
ساخت Conversation
curl --request POST \
--url http://127.0.0.1:8000/v1/conversations \
--header "Content-Type: application/json" \
--data '{
"title": "آموزش PostgreSQL"
}'
نمونه پاسخ:
{
"id": "8766a142-122b-4f57-b07a-8c8cfeab8241",
"title": "آموزش PostgreSQL",
"model_id": "YOUR_MODEL_ID"
}
ارسال پیام
مقدار CONVERSATION_ID را با شناسه واقعی جایگزین کنید:
curl --request POST \
--url http://127.0.0.1:8000/v1/conversations/CONVERSATION_ID/messages \
--header "Content-Type: application/json" \
--data '{
"content": "تفاوت PostgreSQL و Redis چیست؟"
}'
Backend پیام User را ذخیره میکند، تاریخچه را میخواند، درخواست را به درواره میفرستد و پاسخ Assistant را نیز در PostgreSQL ثبت میکند.
دریافت تاریخچه
curl \
http://127.0.0.1:8000/v1/conversations/CONVERSATION_ID/messages
نمونه پاسخ:
{
"data": [
{
"id": "1fd16b42-8b69-43f0-8c55-3f3714434e17",
"role": "user",
"content": "تفاوت PostgreSQL و Redis چیست؟",
"metadata": {},
"created_at": "2026-08-06T12:00:00Z"
},
{
"id": "68e01f32-dca8-41d4-a9af-9381f22a8907",
"role": "assistant",
"content": "PostgreSQL معمولاً منبع اصلی دادههای رابطهای است، در حالی که Redis بیشتر برای Cache، Session و دادههای موقت استفاده میشود.",
"metadata": {
"model": "YOUR_MODEL_ID",
"usage": {}
},
"created_at": "2026-08-06T12:00:02Z"
}
],
"count": 2
}
چرا هنگام فراخوانی API، Transaction باز نگه نداشتیم؟
در کد، Connection پایگاه داده هنگام انتظار برای پاسخ مدل آزاد شده است.
طراحی نامناسب:
شروع Transaction
درج پیام
فراخوانی API با انتظار ۳۰ ثانیه
درج پاسخ
Commit
این کار Connection و Transaction را هنگام عملیات شبکه باز نگه میدارد.
طراحی مقاله:
درج و Commit پیام User
آزاد کردن Connection
فراخوانی API
دریافت Connection جدید
ذخیره پاسخ Assistant
در این مدل اگر فراخوانی مدل شکست بخورد، پیام User همچنان ذخیره است. در پروژه کامل میتوان برای Generation یک Status مانند pending، completed یا failed نگه داشت و Retry کنترلشده ایجاد کرد.
PostgreSQL در معماری هوش مصنوعی
PostgreSQL میتواند این اطلاعات را نگه دارد:
- کاربران
- Conversationها
- پیامها
- Prompt Templateها
- تنظیمات مدل
- Feedback
- Evals
- گزارش Usage
- وضعیت Job
- اسناد
- Metadata
- نتیجه پردازش
- حافظه بلندمدت Agent
Redis میتواند لایه مکمل باشد:
- Cache
- Session
- Rate Limit
- Lock
- Queue
- حافظه کوتاهمدت
Vector Database یا pgvector میتواند برای Similarity Search و RAG استفاده شود.
PostgreSQL → داده اصلی و تاریخچه
Redis → Cache و وضعیت موقت
pgvector → جستوجوی معنایی
درواره → دسترسی API به مدل هوش مصنوعی
اتصال به API درواره
Base URL درواره:
https://api.darvareh.ir/v1
Endpoint مربوط به Chat Completions:
https://api.darvareh.ir/v1/chat/completions
کلید API را فقط در Backend و Environment Variable نگه دارید:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
برای انتخاب Model ID و مشاهده قیمت بهروز، صفحه مدلهای درواره را بررسی کنید.
Migration چیست؟
با تغییر برنامه، Schema پایگاه داده نیز تغییر میکند:
- افزودن Table
- افزودن Column
- ساخت Index
- تغییر Constraint
- تبدیل Data Type
- انتقال داده قدیمی
اجرای دستی تغییرات روی Production قابل اتکا نیست. Migrationها تغییرات Schema را نسخهبندی میکنند.
ابزارهای رایج:
- Alembic برای SQLAlchemy و Python
- Django Migrations
- Prisma Migrate
- Flyway
- Liquibase
- ابزار Migration فریمورکها
هر Migration بهتر است:
- در Version Control باشد
- روی نسخه مشابه Production آزمایش شود
- زمان Lock را بررسی کند
- مسیر Roll-forward داشته باشد
- Backup و Recovery Plan را در نظر بگیرد
Backup و Replication یکسان نیستند
Backup
نسخهای برای بازیابی داده در زمان بعدی است.
Replication
داده را روی Server دیگری کپی میکند تا Availability یا Read Capacity افزایش یابد.
اگر داده اشتباه حذف شود، ممکن است حذف روی Replica نیز تکرار شود. بنابراین Replica جایگزین Backup نیست.
Point-in-time Recovery
با Base Backup و WAL Archive میتوان Database را به زمان مشخصی بازگرداند؛ البته باید از قبل تنظیم و آزمایش شده باشد.
Backup بدون Restore Test قابل اعتماد کامل نیست. باید بازیابی آزمایشی انجام شود.
مانیتورینگ PostgreSQL
Metricهای مهم:
Active Connections
Idle Connections
Transaction Rate
Query Latency
Slow Queries
Cache Hit Ratio
Dead Tuples
Autovacuum Activity
Database Size
Table Size
Index Size
Replication Lag
Lock Wait
Disk Usage
WAL Growth
Checkpoint Activity
Connection Pool Usage
Queryهای کند را با داده واقعی، EXPLAIN و EXPLAIN ANALYZE بررسی کنید. افزایش منابع Server همیشه جایگزین اصلاح Query و Index نیست.
اشتباهات رایج در PostgreSQL
ذخیره همهچیز در JSONB
JSONB انعطافپذیر است، اما داده اصلی و پرتکرار باید Column مشخص داشته باشد.
ساخت Index برای تمام Columnها
Index اضافی هزینه Write و Disk را افزایش میدهد.
نداشتن Foreign Key
اگر رابطه واقعی وجود دارد، حذف Foreign Key میتواند داده Orphan ایجاد کند.
استفاده از OFFSET بزرگ
برای Dataset بزرگ، Keyset Pagination را بررسی کنید.
نگه داشتن Transaction هنگام API Call
Transaction طولانی میتواند Connection و نسخههای Row را برای مدت بیشتری نگه دارد.
نداشتن Connection Pool
ساخت Connection برای هر Request باعث افزایش سربار میشود.
Pool بسیار بزرگ
Connection بیش از حد میتواند PostgreSQL را تحت فشار قرار دهد.
استفاده از Float برای مبلغ
برای مبلغ دقیق از NUMERIC یا واحد صحیح کوچکتر استفاده کنید.
ترکیب زمان Local و UTC
زمان را با قرارداد مشخص ذخیره و نمایش دهید. TIMESTAMPTZ و UTC معمولاً انتخاب مناسبی هستند.
ساخت SQL با String Formatting
پارامترها را با Binding ارسال کنید.
نادیده گرفتن Autovacuum
Autovacuum برای سلامت بلندمدت Database مهم است.
اجرای Migration سنگین بدون بررسی
بعضی تغییرات Schema میتوانند Lock طولانی ایجاد کنند.
نداشتن Backup آزمایششده
وجود فایل Backup بهتنهایی کافی نیست؛ Restore باید آزمایش شود.
ذخیره فایل بزرگ در Database بدون ارزیابی
در بسیاری از پروژهها فایل در Object Storage ذخیره و URL و Metadata آن در PostgreSQL نگهداری میشود.
ذخیره تاریخچه نامحدود در Context مدل
نگهداری پیام در PostgreSQL به این معنی نیست که باید تمام تاریخچه در هر Request به مدل ارسال شود. تاریخچه باید محدود، خلاصه یا بر اساس نیاز بازیابی شود.
چکلیست PostgreSQL برای Production
پیش از انتشار پروژه این موارد را بررسی کنید:
- Schema بر اساس نیاز واقعی طراحی شده است
- Primary Key برای Tableها وجود دارد
- Foreign Keyهای لازم تعریف شدهاند
- Constraintهای مهم در Database وجود دارند
- Data Type مناسب انتخاب شده است
- زمانها با قرارداد مشخص ذخیره میشوند
- مبلغ با نوع دقیق نگهداری میشود
- Queryهای ورودی Parameterized هستند
- Indexها بر اساس Query واقعی ساخته شدهاند
- Indexهای بدون استفاده بررسی میشوند
- Queryهای مهم با EXPLAIN تحلیل شدهاند
- Pagination برای فهرستهای بزرگ وجود دارد
- JSONB فقط برای داده مناسب استفاده شده است
- GIN یا Expression Index در صورت نیاز وجود دارد
- Connection Pool فعال است
- اندازه Pool بر اساس کل Instanceها تعیین شده است
- Transactionهای طولانی کنترل میشوند
- Timeout برای Queryها و Connectionها وجود دارد
- Migrationها نسخهبندی شدهاند
- Migration روی داده مشابه Production تست شده است
- Backup خودکار فعال است
- Restore آزمایش شده است
- Replication جایگزین Backup فرض نشده است
- Disk و WAL مانیتور میشوند
- Autovacuum فعال و مانیتور شده است
- Slow Queryها ثبت میشوند
- تعداد Connectionها مانیتور میشود
- Secretها داخل Repository نیستند
- تاریخچه ارسالی به مدل محدود میشود
- خروجی مدل پیش از استفاده ساختاری اعتبارسنجی میشود
پرسشهای متداول
PostgreSQL چیست؟
PostgreSQL یک سیستم مدیریت پایگاه داده رابطهای متنباز است که از SQL، Transaction، Constraint، Index، JSONB و Extension پشتیبانی میکند.
تفاوت PostgreSQL و Postgres چیست؟
Postgres نام کوتاه و رایج PostgreSQL است. هر دو به یک پروژه اشاره میکنند.
آیا PostgreSQL رایگان است؟
PostgreSQL پروژهای متنباز است. برای شرایط دقیق استفاده و توزیع باید مجوز رسمی همان نسخه و خدمات زیرساختی انتخابشده بررسی شود.
PostgreSQL بهتر است یا MySQL؟
هر دو Databaseهای معتبر و پرکاربردی هستند. انتخاب به تیم، Hosting، قابلیتهای موردنیاز، سازگاری نرمافزار و تجربه عملی بستگی دارد. PostgreSQL برای SQL پیشرفته، JSONB، Extension و Data Typeهای متنوع شناخته میشود.
PostgreSQL بهتر است یا Redis؟
این مقایسه مستقیم نیست. PostgreSQL معمولاً منبع اصلی داده رابطهای است و Redis بیشتر برای Cache، Session، Queue و داده موقت استفاده میشود. بسیاری از پروژهها از هر دو استفاده میکنند.
تفاوت JSON و JSONB چیست؟
JSON شکل متنی ورودی را بیشتر حفظ میکند. JSONB داده را در ساختاری مناسبتر برای پردازش، Operator و Index ذخیره میکند.
آیا PostgreSQL برای هوش مصنوعی مناسب است؟
بله. میتوان کاربران، پیامها، Promptها، Usage، Feedback، اسناد و Metadata را در PostgreSQL ذخیره کرد. با Extensionهایی مانند pgvector نیز میتوان Vectorها را برای بعضی پروژههای RAG نگه داشت.
آیا میتوان فایل را در PostgreSQL ذخیره کرد؟
از نظر فنی امکان نگهداری داده باینری وجود دارد، اما در بسیاری از معماریها فایل در Object Storage و Metadata آن در PostgreSQL ذخیره میشود.
چرا به Connection Pool نیاز داریم؟
ایجاد Connection جدید هزینه دارد. Pool تعداد محدودی Connection را نگه میدارد و بین Requestها دوباره استفاده میکند.
آیا هر Column به Index نیاز دارد؟
خیر. Index باید بر اساس Queryهای واقعی ساخته شود. Index اضافی باعث افزایش مصرف Disk و هزینه Write میشود.
آیا PostgreSQL جایگزین Vector Database است؟
با pgvector میتوان Vector Search را داخل PostgreSQL انجام داد. مناسب بودن آن به حجم داده، Query، Latency و معماری بستگی دارد. در بعضی پروژهها Vector Database تخصصی انتخاب مناسبتری است.
آدرس API درواره چیست؟
https://api.darvareh.ir/v1
Endpoint مربوط به Chat Completions:
https://api.darvareh.ir/v1/chat/completions
قیمت مدلهای درواره را از کجا ببینیم؟
فهرست Model IDها و قیمت بهروز در صفحه مدلهای درواره قرار دارد.
جمعبندی
PostgreSQL یک Database قدرتمند برای نگهداری پایدار دادههای Backend، اپلیکیشن و سرویسهای هوش مصنوعی است. پشتیبانی از SQL، رابطه، Constraint، Transaction، Index و JSONB باعث میشود بتوان دادههای ساختاریافته و بخشی از دادههای نیمهساختاریافته را در یک سیستم مدیریت کرد.
در پروژه عملی این مقاله، یک API گفتوگو با FastAPI ساختیم که Conversation و Messageها را در PostgreSQL ذخیره میکند، تاریخچه اخیر را میخواند و برای تولید پاسخ به API درواره متصل میشود.
برای ساخت یک سیستم قابل اتکا، فقط ایجاد Table کافی نیست. باید Data Type، Constraint، Index، Transaction، Connection Pool، Migration، Backup، Autovacuum و Monitoring نیز متناسب با Workload طراحی شوند.
برای دریافت API Key و افزودن مدلهای هوش مصنوعی به Backend خود، به وبسایت درواره مراجعه کنید. فهرست مدلها، Model ID و قیمتهای بهروز نیز در صفحه مدلهای درواره در دسترس است.
منابع
- مستندات رسمی PostgreSQL
- راهنمای Data Definition در PostgreSQL
- راهنمای Data Typeهای PostgreSQL
- راهنمای Constraintها
- راهنمای Indexها در PostgreSQL
- مستندات B-tree Index
- مستندات JSON و JSONB
- توابع و Operatorهای JSON
- مستندات Psycopg 3 برای Python
- راهنمای Connection Pool در Psycopg
مقالات مرتبط
- حافظه در AI Agent؛ ساخت Memory با PostgreSQL، Redis و Vector Database
- Database Migration با PostgreSQL، SQLAlchemy و Alembic
- تبدیل متن به SQL با هوش مصنوعی؛ آموزش Text-to-SQL
- ساخت AI Agent با Python، FastAPI و API درواره
- ساخت RAG با LangChain، LlamaIndex و API درواره
- پردازش غیرهمزمان API هوش مصنوعی با Celery، Redis و Worker
- ساخت API هوش مصنوعی آماده محیط Production
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.