PostgreSQL: نظام إضافات PostgreSQL وFDW والبحث المتجهي
آخر تحديث: 2026-08-26
1. ما ستتعلمه
- فهم آلية الإضافات في PostgreSQL (CREATE EXTENSION)
- إتقان الإضافات الشائعة: uuid-ossp، pgcrypto، pg_trgm، pg_stat_statements
- تعلم Foreign Data Wrappers (FDW) للاستعلامات عبر قواعد البيانات
- التدرب على تكوين واستخدام postgres_fdw و file_fdw
- إتقان pgvector تخزين المتجهات والبحث بالتشابه
- فهم أفضل ممارسات إدارة الإضافات
2. القصة
تحتاج شركة Alice للتجارة الإلكترونية إلى القيام بثلاثة أشياء: (1) دمج توصيات منتجات الذكاء الاصطناعي، مما يتطلب بحثًا متجهيًا؛ (2) ترحيل بيانات MySQL القديمة إلى PG، مما يتطلب استعلامات عبر قواعد البيانات؛ (3) تسريع البحث التقريبي البطيء، مما يتطلب pg_trgm. وجدت أن نظام إضافات PostgreSQL يحل كل شيء في مكان واحد — pgvector يخزن تضمينات المنتجات، و postgres_fdw يتصل بـ MySQL للترحيل، و pg_trgm يعزز أداء البحث التقريبي. قاعدة بيانات واحدة تستبدل Elasticsearch + الوسيط + خدمة توصية مبنية ذاتيًا.
3. المفهوم: نظرة عامة على آلية الإضافات
(1) ما هي إضافة PostgreSQL
الإضافة هي نظام المكونات الإضافية في PostgreSQL — تجمع كائنات SQL المرتبطة (الدوال، الأنواع، العوامل، طرق الفهرس) كوحدة واحدة يمكن تثبيتها وإلغاء تثبيتها بأمر واحد.
-- سرد الإضافات المتاحة
SELECT name, default_version, installed_version, comment
FROM pg_available_extensions
ORDER BY name;
(2) أوامر إدارة الإضافات
| الأمر | الغرض |
|---|---|
CREATE EXTENSION ext_name |
تثبيت إضافة |
CREATE EXTENSION IF NOT EXISTS ext_name |
تثبيت متكرر آمن |
CREATE EXTENSION ext_name VERSION '1.2' |
تثبيت إصدار محدد |
DROP EXTENSION ext_name |
إلغاء تثبيت إضافة (CASCADE للاحتفاظ بال��كوين) |
ALTER EXTENSION ext_name UPDATE TO '2.0' |
ترقية إضافة |
\dx (psql) |
سرد الإضافات المثبتة |
(3) المتطلبات الأساسية لتثبيت الإضافات
| الشرط | الوصف |
|---|---|
| ملف مكتبة مشتركة | .so / .dll يجب أن يكون في shared_preload_libraries أو dynamic_library_path |
| ملف تحكم | extension_name.control في SHAREDIR/extension/ |
| سكريبت SQL | extension_name--version.sql يعرف الكائنات |
| الصلاحيات | تحتاج CREATE على قاعدة البيانات الحالية + مستخدم مميز (لبعض الإضافات) |
4. العملية: الإضافات الشائعة
(1) ▶ مثال
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- توليد UUID v4 (عشوائي)
SELECT uuid_generate_v4();
-- توليد UUID v1 (معتمد على الوقت)
SELECT uuid_generate_v1();
-- استخدام كقيمة افتراضية لعمود
CREATE TABLE api_keys (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
user_id BIGINT NOT NULL,
key_name TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
Output:
CREATE TABLE
(2) ▶ مثال
CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- تجزئة كلمة المرور مع ملح
SELECT crypt('MySecret123', gen_salt('bf'));
-- التحقق من كلمة المرور
SELECT crypt('MySecret123', stored_hash) = stored_hash AS is_match;
-- تشفير AES
SELECT encode(encrypt('credit card data'::bytea,
'secret_key_16bytes'::bytea, 'aes'), 'hex');
-- توليد رمز عشوائي
SELECT encode(gen_random_bytes(32), 'hex');
Output:
CREATE TABLE
(3) ▶ مثال
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- عرض درجة التشابه
SELECT similarity('PostgreSQL', 'Postgres');
-- فهرس GIN trigram للبحث التقريبي السريع
CREATE INDEX idx_products_name_trgm ON products
USING GIN (name gin_trgm_ops);
-- بحث تقريبي مع عتبة
SELECT name, similarity(name, 'iphon') AS score
FROM products
WHERE name % 'iphon'
ORDER BY score DESC;
name | score
---------------+-------
iPhone 15 Pro | 0.42
iPhone 14 | 0.38
(2 rows)
(4) ▶ مثال
-- يجب أن يكون في shared_preload_libraries أولاً
-- postgresql.conf: shared_preload_libraries = 'pg_stat_statements'
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- أفضل 10 استعلامات حسب وقت التنفيذ الإجمالي
SELECT query,
calls,
round(total_exec_time::numeric, 2) AS total_ms,
round(mean_exec_time::numeric, 2) AS avg_ms,
rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
-- إعادة تعيين الإحصائيات
SELECT pg_stat_statements_reset();
Output:
result
----------
42.50
(1 row)
(5) ▶ مثال
CREATE EXTENSION IF NOT EXISTS postgis;
-- تخزين هندسة النقطة
CREATE TABLE stores (
id SERIAL PRIMARY KEY,
name TEXT,
location GEOMETRY(POINT, 4326)
);
-- إدراج إحداثيات (خط طول، خط عرض)
INSERT INTO stores (name, location)
VALUES ('Dubai Mall', ST_SetSRID(ST_MakePoint(55.2796, 25.1972), 4326));
-- العثور على متاجر ضمن 5 كم
SELECT name,
ST_Distance(location::geography,
ST_SetSRID(ST_MakePoint(55.2700, 25.2000), 4326)::geography
) AS distance_m
FROM stores
WHERE ST_DWithin(location::geography,
ST_SetSRID(ST_MakePoint(55.2700, 25.2000), 4326)::geography, 5000);
Output:
INSERT 0 1
5. المفهوم: Foreign Data Wrappers
(1) معمارية FDW
flowchart LR
A["PG محلي<br/>postgres_fdw"] -->|"CREATE SERVER"| B["PG بعيد<br/>(أو MySQL/Oracle)"]
A -->|"CREATE SERVER"| C["file_fdw<br/>(ملفات CSV/سجل)"]
A -->|"CREATE SERVER"| D["FDW أخرى<br/>(Redis/MongoDB...)"]
B --> E["IMPORT FOREIGN SCHEMA"]
C --> F["CREATE FOREIGN TABLE"]
D --> G["JOIN عبر الأنظمة"]
| اسم FDW | مصدر البيانات الهدف | حالة الاستخدام |
|---|---|---|
| postgres_fdw | PostgreSQL بعيد | استعلامات عبر قواعد البيانات، ترحيل البيانات |
| mysql_fdw | MySQL بعيد | ترحيل MySQL→PG |
| file_fdw | ملفات CSV محلية | تحليل السجلات، استيراد البيانات |
| redis_fdw | Redis | استعلامات التخزين المؤقت |
| mongo_fdw | MongoDB | استعلامات المستندات |
(2) خطوات تكوين FDW
| الخطوة | الأمر |
|---|---|
| 1. تثبيت الإضافة | CREATE EXTENSION postgres_fdw |
| 2. إنشاء SERVER | CREATE SERVER remote FOREIGN DATA WRAPPER postgres_fdw OPTIONS (...) |
| 3. إنشاء USER MAPPING | CREATE USER MAPPING FOR local_user SERVER remote OPTIONS (...) |
| 4. إنشاء FOREIGN TABLE | CREATE FOREIGN TABLE ft_xxx SERVER remote OPTIONS (...) |
| 5. أو استيراد SCHEMA كامل | IMPORT FOREIGN SCHEMA public LIMIT TO (orders) FROM SERVER remote INTO remote_schema |
6. العملية: postgres_fdw استعلام عبر قواعد البيانات
(6) ▶ مثال
-- الخطوة 1: تثبيت الإضافة
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
-- الخطوة 2: إنشاء اتصال الخادم
CREATE SERVER legacy_db
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host '192.168.1.100', port '5432', dbname 'legacy');
-- الخطوة 3: ربط المستخدم المحلي ببيانات اعتماد بعيدة
CREATE USER MAPPING FOR current_user
SERVER legacy_db
OPTIONS (user 'admin', password 'secret123');
-- الخطوة 4: استيراد المخطط الخارجي
IMPORT FOREIGN SCHEMA public
LIMIT TO (users, products, orders)
FROM SERVER legacy_db
INTO legacy_schema;
-- الآن استعلم عن الجداول البعيدة كما لو كانت محلية
SELECT u.name, COUNT(o.id) AS order_count
FROM legacy_schema.users u
JOIN legacy_schema.orders o ON u.id = o.user_id
GROUP BY u.name;
Output:
count
-------
5
(1 row)
(7) ▶ مثال
-- محلي: كتالوج منتجات PG الجديد
-- بعيد: بيانات طلبات MySQL القديمة عبر postgres_fdw
SELECT p.name,
SUM(loi.quantity) AS total_sold,
SUM(loi.quantity * loi.unit_price) AS revenue
FROM products p
JOIN legacy_schema.order_items loi ON p.id = loi.product_id
WHERE p.category = 'Electronics'
GROUP BY p.name
ORDER BY revenue DESC;
Output:
result
----------
42.50
(1 row)
(8) ▶ مثال
CREATE EXTENSION IF NOT EXISTS file_fdw;
CREATE SERVER csv_server
FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE access_logs_csv (
ip_address TEXT,
request_time TIMESTAMP,
method TEXT,
path TEXT,
status_code INT,
response_time NUMERIC
) SERVER csv_server
OPTIONS (filename '/var/log/nginx/access.csv', format 'csv', header 'true');
-- تحليل سجلات nginx بـ SQL
SELECT path,
COUNT(*) AS hits,
AVG(response_time) AS avg_ms
FROM access_logs_csv
WHERE status_code = 200
GROUP BY path
ORDER BY hits DESC
LIMIT 20;
Output:
count
-------
5
(1 row)
(9) ▶ مثال
-- ترحيل المستخدمين من القديم إلى المحلي
INSERT INTO users (name, email, created_at)
SELECT name, email, created_at
FROM legacy_schema.users
WHERE id NOT IN (SELECT legacy_id FROM users);
-- استخدام dblink للمزامنة التدريجية
CREATE EXTENSION IF NOT EXISTS dblink;
SELECT dblink_connect('legacy', 'host=192.168.1.100 dbname=legacy user=admin password=secret123');
SELECT * FROM dblink('legacy',
'SELECT id, name, email FROM users WHERE created_at > now() - interval ''1 day'''
) AS t(id BIGINT, name TEXT, email TEXT);
Output:
INSERT 0 1
7. المفهوم: pgvector البحث المتجهي
(1) كيفية عمل البحث المتجهي
تقوم نماذج الذكاء الاصطناعي بترميز النص/الصور إلى متجهات عالية الأبعاد (تضمينات)، وتمثل المسا��ة بين المتجهات التشابه الدلالي. يتيح pgvector لـ PostgreSQL تخزين واسترجاع المتجهات أصليًا.
| مقياس المسافة | الصيغة | حالة الاستخدام |
|---|---|---|
| مسافة L2 (<=>) | المسافة الإقليدية | المسافة المكانية |
| الجداء الداخلي (<#>) | الجداء النقطي | المتجهات المعيارية |
| مسافة جيب التمام (<=>) | 1 - cos(θ) | التشابه الدلالي |
(2) اختيار فهرس pgvector
flowchart TD
A["تم إنشاء عمود المتجه"] --> B{"الصفوف < 10K؟"}
B -->|نعم| C["بحث دقيق<br/>(لا حاجة لفهرس)"]
B -->|لا| D{"متطلب الاستدعاء؟"}
D -->|"استدعاء عال (> 99%)"| E["IVFFlat<br/>(probes=lists)"]
D -->|"سريع + استدعاء جيد"| F["HNSW<br/>(ضبط ef_search)"]
E --> G["CREATE INDEX ... USING ivfflat<br/>(vector_cosine_ops)"]
F --> H["CREATE INDEX ... USING hnsw<br/>(vector_cosine_ops)"]
| الفهرس | سرعة البناء | سرعة الاستعلام | الاستدعاء | الأفضل لـ |
|---|---|---|---|---|
| بدون فهرس (قوة غاشمة) | غير متاح | بطيء | 100% | < 10K |
| IVFFlat | متوسط | سريع | 95-99% | 10K-1M |
| HNSW | بطيء | الأسرع | 97-99.5% | 100K-10M+ |
8. العملية: pgvector عمليًا
(10) ▶ مثال
-- تثبيت pgvector
CREATE EXTENSION IF NOT EXISTS vector;
-- جدول منتجات مع متجه تضمين (1536 بعدًا لـ OpenAI)
CREATE TABLE products_vec (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
category TEXT,
price NUMERIC(10,2),
embedding vector(1536)
);
Output:
CREATE TABLE
(11) ▶ مثال
-- إدراج منتج مع تضمين
INSERT INTO products_vec (name, category, price, embedding)
VALUES ('Wireless Headphones', 'Electronics', 79.99,
'[0.012, -0.034, 0.056, ...]'::vector);
-- العثور على أفضل 5 منتجات متشابهة بمسافة جيب التمام
SELECT p.id, p.name, p.category, p.price,
p.embedding <=> '[0.015, -0.030, 0.050, ...]'::vector AS distance
FROM products_vec p
ORDER BY p.embedding <=> '[0.015, -0.030, 0.050, ...]'::vector
LIMIT 5;
Output:
INSERT 0 1
(12) ▶ مثال
-- فهرس HNSW للبحث التقريبي السريع
CREATE INDEX idx_products_vec_hnsw ON products_vec
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- ضبط دقة البحث مقابل السرعة
SET hnsw.ef_search = 100;
-- استعلام مع الفهرس (أسرع بكثير على مجموعات البيانات الكبيرة)
SELECT name,
embedding <=> '[0.015, -0.030, 0.050, ...]'::vector AS distance
FROM products_vec
ORDER BY embedding <=> '[0.015, -0.030, 0.050, ...]'::vector
LIMIT 10;
Output:
CREATE TABLE
(13) ▶ مثال
-- تخزين تضمينات المنتجات من نموذج الذكاء الاصطناعي
INSERT INTO products_vec (name, category, price, embedding)
VALUES
('Running Shoes', 'Sports', 129.99, array_to_vec(ARRAY[0.1,0.2,0.3])::vector),
('Yoga Mat', 'Sports', 39.99, array_to_vec(ARRAY[0.11,0.19,0.31])::vector),
('Bluetooth Speaker', 'Electronics', 49.99, array_to_vec(ARRAY[0.5,0.1,0.2])::vector);
-- شاهد المستخدم "Running Shoes"، أوصِ بعناصر مشابهة
WITH target AS (
SELECT embedding FROM products_vec WHERE name = 'Running Shoes'
)
SELECT p.name, p.category, p.price,
p.embedding <=> (SELECT embedding FROM target) AS similarity
FROM products_vec p
WHERE p.name != 'Running Shoes'
ORDER BY similarity
LIMIT 3;
Output:
INSERT 0 1
9. العملية: أفضل ممارسات إدارة الإضافات
(1) إدارة الإصدارات
-- التحقق من إصدار الإضافة الحالي
SELECT extname, extversion FROM pg_extension ORDER BY extname;
-- ترقية إضافة
ALTER EXTENSION pgvector UPDATE TO '0.7.0';
-- ترقية جميع الإضافات
SELECT extname,
installed_version,
default_version
FROM pg_available_extensions
WHERE installed_version IS NOT NULL
AND installed_version != default_version;
(2) إدارة الصلاحيات
| السيناريو | الممارسة الموصى بها |
|---|---|
| تثبيت إضافة في الإنتاج | مستخدم مميز يشغل CREATE EXTENSION |
| استخدام مستخدم عادي | GRANT USAGE ON SCHEMA / صلاحية تنفيذ الدالة |
| اتصال FDW | USER MAPPING يخزن بيانات الاعتماد، لا تصلب كلمات المرور |
| ترقية إضافة | اختبر في staging أولاً، ثم شغل في الإنتاج |
10. مثال شامل
-- إعداد كامل: إضافات + FDW + pgvector للتجارة الإلكترونية بالذكاء الاصطناعي
-- الخطوة 1: تثبيت الإضافات الأساسية
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS pgvector;
-- الخطوة 2: كتالوج منتجات مع بحث متجهي
CREATE TABLE products_ai (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
name TEXT NOT NULL,
category TEXT,
price NUMERIC(10,2),
attrs JSONB DEFAULT '{}',
embedding vector(1536)
);
-- الخطوة 3: فهرس GIN للبحث التقريبي بالاسم
CREATE INDEX idx_products_name_trgm ON products_ai
USING GIN (name gin_trgm_ops);
-- الخطوة 4: فهرس HNSW لتشابه المتجهات
CREATE INDEX idx_products_embedding ON products_ai
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- الخطوة 5: الاتصال بقاعدة البيانات القديمة عبر FDW
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
CREATE SERVER legacy_db FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host '192.168.1.50', port '5432', dbname 'legacy_shop');
CREATE USER MAPPING FOR current_user SERVER legacy_db
OPTIONS (user 'migrate_user', password 'secure_pass');
IMPORT FOREIGN SCHEMA public LIMIT TO (old_products, old_users)
FROM SERVER legacy_db INTO legacy;
-- الخطوة 6: ترحيل وتحويل البيانات
INSERT INTO products_ai (name, category, price, attrs)
SELECT name, category, price,
jsonb_build_object('weight_kg', weight, 'color', color)
FROM legacy.old_products
WHERE active = true;
-- الخطوة 7: بحث مدمج تقريبي + متجهي
SELECT p.name, p.price,
similarity(p.name, 'wireless earbuds') AS text_score,
p.embedding <=> '[0.015,-0.030,0.050]'::vector AS vec_dist
FROM products_ai p
WHERE p.name % 'wireless earbuds'
ORDER BY vec_dist ASC
LIMIT 5;
❓ أسئلة شائعة
GRANT CREATE ON DATABASE + آلية الإضافة الموثوقة.use_remote_estimate=on يجعل البعيد يولد تقديرات التكلفة ويحسن جودة الخطة. للترحيل الجماعي، فضل COPY على FDW.📖 ملخص
- آلية الإضافات في PostgreSQL (CREATE EXTENSION) تمكن إدارة بأسلوب المكونات الإضافية
- uuid-ossp (UUID)، pgcrypto (تشفير)، pg_trgm (بحث تقريبي)، pg_stat_statements (تحليل الاستعلامات البطيئة) هي الإضافات الأربع الأكثر شيوعًا
- Foreign Data Wrappers (FDW) تتيح لـ PG الاستعلام عن مصادر بيانات خارجية كما لو كانت جداول محلية
- postgres_fdw يناسب استعلامات PG عبرية والترحيل؛ file_fdw يناسب تحليل سجلات CSV
- pgvector يوفر نوع بيانات متجه + بحث بالتشابه؛ فهرس HNSW هو الأسرع استعلامًا
- إدارة الإضافات تحتاج اهتمامًا بالإصدار والصلاحيات والتدقيق الأمني وعملية مراجعة الإنتاج
📝 تمارين
-
⭐ قم بتثبيت إضافتي uuid-ossp و pgcrypto، أنشئ جدول
api_tokensباستخدام UUID كمفتاح أساسي وcrypt()لتخزين تجزئة كلمة المرور، واكتب استعلامًا يتحقق من كلمة المرور. -
⭐⭐ قم بتكوين postgres_fdw للاتصال بقاعدة بيانات PG بعيدة (استخدم Docker للمحاكاة)، استورد جدول
productsالبعيد، واكتب استعلام JOIN عبر قواعد البيانات: جدول orders المحلي + جدول products البعيد. -
⭐⭐⭐ صمم بحث منتجات بمحرك مزدوج: pg_trgm يتعامل مع البحث النصي التقريبي، pgvector يتعامل مع البحث المتجهي الدلالي. اكتب دالة بحث مدمجة
search_products(keyword TEXT, query_vec vector, limit_count INT)تدمج درجة النص ومسافة المتجه للترتيب، واختبر كيف تختلف النتائج عبر الأوزان المختلفة.