لماذا قاعدة بيانات للسلاسل الزمنية
السلسلة الزمنية تتابع من القيم المؤرَّخة: حرارة مجس كل عشر ثوانٍ، أو عدد الطلبات في الدقيقة. تُكتب كثيرًا ونادرًا ما تُعدَّل، ويُستعلم عنها دائمًا تقريبًا حسب فترة زمنية.
بُنيت QuestDB حول هذا الاستخدام. لكل جدول عمود زمني محدد، ويُقسَّم إلى أقسام (حسب اليوم أو الساعة)، ما يُبقي الاستعلامات على فترة ما سريعة ويجعل حذف البيانات القديمة فوريًا. وتبقى لغتها SQL، مع بعض الكلمات المفتاحية الخاصة بالزمن.
ما تقدّمه QuestDB
- SQL للسلاسل الزمنية —
SAMPLE BYوLATEST ONوASOF JOINإضافةً إلى SQL القياسية. - استيعاب متواصل — InfluxDB Line Protocol فوق HTTP، مع عملاء رسمية لـ Python وGo وJava وNode.js وRust و.NET.
- واجهة ويب — محرر SQL، واستيراد CSV بالسحب والإفلات، ورسوم بيانية للنتائج.
- واجهة REST — استعلامات واستيراد وتصدير CSV عبر HTTP.
- بروتوكول PostgreSQL — مشغّلات وأدوات PostgreSQL المعتادة لديك.
- أقسام زمنية — استعلامات سريعة على فترة، وحذف البيانات القديمة بأمر واحد.
QuestDB مقارنةً بجدول PostgreSQL تقليدي
مرّر الجدول أفقيًا
| QuestDB | PostgreSQL وحده | |
|---|---|---|
| مصممة لـ | قياسات مؤرَّخة متواصلة | بيانات علائقية عامة |
| التجميع حسب الفترة | SAMPLE BY 1h | date_trunc وGROUP BY |
| آخر قيمة لكل سلسلة | LATEST ON | استعلام فرعي أو DISTINCT ON |
| محاذاة تدفقين زمنيًا | ASOF JOIN | ربط جانبي يجب كتابته |
| حذف السجل القديم | DROP PARTITION | DELETE ثم VACUUM |
| الاستيعاب | Line Protocol وCSV وSQL | SQL وCOPY |
المتطلبات المسبقة
خادم افتراضي بنظام Ubuntu 24.04 بذاكرة 2 غيغابايت على الأقل وقرص 20 غيغابايت، مع تثبيت Docker وإضافة Compose، واسم (نطاق فرعي) يشير إلى الخادم من أجل HTTPS. تعمل QuestDB على JVM: خطّط لذاكرة 4 غيغابايت إذا كنت تستوعب بيانات كثيرة أو تنفّذ استعلامات ثقيلة.
تثبيت QuestDB باستخدام Docker Compose
تجهيز المجلد وكلمة المرور
أنشئ مجلد التطبيق وملف
.envيحمل كلمة مرور المستخدمadmin.mkdir -p /opt/questdb && cd /opt/questdb echo "ADMIN_PASSWORD=$(openssl rand -hex 16)" > .env chmod 600 .envكتابة ملف docker-compose.yml
لا تُنشر المنافذ إلا على واجهة loopback: وحده nginx يصل إلى الواجهة وواجهة البرمجة. يحمي
QDB_HTTP_USERوQDB_HTTP_PASSWORDواجهة HTTP كاملة، ويستبدلQDB_PG_PASSWORDكلمة مرور المصنع لبروتوكول PostgreSQL. يُوقَف الاستيعاب عبر TCP، لأنه بلا مصادقة في النسخة مفتوحة المصدر، ويعطّلQDB_TELEMETRY_ENABLEDالقياس عن بُعد المجهول.services: questdb: image: questdb/questdb:10.1.0 restart: unless-stopped ports: - "127.0.0.1:9000:9000" - "127.0.0.1:8812:8812" environment: QDB_HTTP_USER: "admin" QDB_HTTP_PASSWORD: "${ADMIN_PASSWORD}" QDB_PG_USER: "admin" QDB_PG_PASSWORD: "${ADMIN_PASSWORD}" QDB_LINE_TCP_ENABLED: "false" QDB_HTTP_SETTINGS_READONLY: "true" QDB_TELEMETRY_ENABLED: "false" volumes: - questdb-data:/var/lib/questdb volumes: questdb-data: {}التشغيل والتحقق
يستغرق التشغيل بضع ثوانٍ. بدون بيانات اعتماد تجيب الواجهة بالرمز 401، ومعها تعيد الإصدار المثبّت.
docker compose up -d curl -s -o /dev/null -w '%{http_code}\n' 'http://127.0.0.1:9000/exec?query=SELECT%201' curl -u admin:VOTRE_MOT_DE_PASSE 'http://127.0.0.1:9000/exec?query=SELECT%20build()'النشر عبر HTTPS باستخدام nginx
في المضيف الافتراضي لنطاقك (بشهادة من certbot)، وجّه الطلبات إلى المنفذ المحلي. ارفع قيمة
client_max_body_sizeمن أجل استيراد ملفات CSV.location / { proxy_pass http://127.0.0.1:9000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 256m; }
بدون QDB_HTTP_USER تكون الواجهة مفتوحة للجميع
عند عدم تعريف مستخدم، تقبل QuestDB كل طلب HTTP: يستطيع أي شخص يعرف العنوان قراءة بياناتك وإنشاء الجداول أو تعديلها أو حذفها بلغة SQL. أبقِ المصادقة مفعّلة، وغيّر كلمة مرور المصنع لبروتوكول PostgreSQL، ولا تنشر أبدًا المنافذ 9000 أو 8812 أو 9009 مباشرة على عنوان IP العام: مرّ دائمًا عبر الوكيل العكسي HTTPS.
إرسال البيانات
أبسط طريقة هي InfluxDB Line Protocol فوق HTTP: سطر لكل قياس، يحمل اسم الجدول والوسوم والقيم. يُنشأ الجدول وأعمدته مع أول كتابة.
curl -u admin:VOTRE_MOT_DE_PASSE -X POST \
--data-binary 'capteurs,sonde=s1 temperature=21.5' \
https://votre-domaine/writeومن شيفرتك، يجمّع العميل الرسمي الأسطر ويتولّى إعادة المحاولة. في Python، بعد pip install questdb:
from questdb.ingress import Sender, TimestampNanos
conf = "https::addr=votre-domaine:443;username=admin;password=VOTRE_MOT_DE_PASSE;"
with Sender.from_conf(conf) as sender:
sender.row(
"capteurs",
symbols={"sonde": "s1"},
columns={"temperature": 21.5},
at=TimestampNanos.now(),
)الاستعلام عن سلاسلك بلغة SQL
في واجهة الويب أو عبر واجهة REST (/exec?query=…)، تعمل SQL القياسية، وتغنيك امتدادات الزمن عن الاستعلامات المعقدة.
-- Moyenne horaire par sonde
SELECT timestamp, sonde, avg(temperature)
FROM capteurs
SAMPLE BY 1h;
-- Dernière valeur connue de chaque sonde
SELECT * FROM capteurs LATEST ON timestamp PARTITION BY sonde;ويمكن لبرنامج يعمل على الخادم نفسه الاتصال أيضًا عبر بروتوكول PostgreSQL على 127.0.0.1:8812، بقاعدة qdb والمستخدم admin وكلمة المرور نفسها.
تصميم جدول السلاسل الزمنية بشكل صحيح
يكفي الإنشاء التلقائي مع أول كتابة للبدء. أما الجدول المعدّ للبقاء طويلًا فعرّفه بنفسك: تختار الأنواع والعمود الزمني وطريقة التقسيم.
CREATE TABLE capteurs (
timestamp TIMESTAMP,
sonde SYMBOL,
temperature DOUBLE
) TIMESTAMP(timestamp) PARTITION BY DAY WAL
DEDUP UPSERT KEYS(timestamp, sonde);ثلاثة اختيارات مهمة. العمود الزمني المحدد (TIMESTAMP(...)) هو الذي ترتّب QuestDB البيانات وتقسّمها وفقه: تعتمد عليه كل امتدادات الزمن. النوع SYMBOL يناسب الوسوم المتكررة (اسم المجس، رمز الجهاز، رمز السهم): تُخزَّن القيمة مرة واحدة ويُشار إليها، ما يوفّر القرص ويسرّع التصفية. التقسيم (PARTITION BY DAY أو HOUR) يُختار حسب الحجم: يجب أن يبقى القسم بحجم معقول، وهو الوحدة التي ستحذفها لاحقًا.
تجعل العبارة DEDUP UPSERT KEYS الاستيعاب متساوي الأثر: إذا أعاد عميل إرسال القياس نفسه بعد انقطاع الشبكة، يُستبدل السطر بدل أن يتكرر.
استيراد ملفات CSV وتصديرها
تقبل الواجهة ملفات CSV بالسحب والإفلات. ومن سطر الأوامر، يستورد /imp ملفًا إلى جدول ويصدّر /exp نتيجة استعلام.
curl -u admin:VOTRE_MOT_DE_PASSE -F [email protected] 'https://votre-domaine/imp?name=mesures'
curl -u admin:VOTRE_MOT_DE_PASSE -G --data-urlencode 'query=SELECT * FROM mesures' https://votre-domaine/exp > export.csvالاحتفاظ بالبيانات والقرص والنسخ الاحتياطي
لا شيء ينتهي من تلقاء نفسه. احذف الأقسام القديمة بلغة SQL وجدول الأمر عند الحاجة. وللحصول على نسخة احتياطية متسقة، جمّد حالة القاعدة أثناء نسخ وحدة التخزين questdb-data.
ALTER TABLE capteurs DROP PARTITION
WHERE timestamp < dateadd('d', -90, now());
CHECKPOINT CREATE;
-- copiez le volume questdb-data, puis :
CHECKPOINT RELEASE;تحديث QuestDB
اقرأ ملاحظات الإصدار قبل الانتقال إلى إصدار رئيسي جديد، ثم استبدل وسم الصورة وأعد التشغيل: تبقى البيانات في وحدة التخزين محفوظة.
cd /opt/questdb
# changez le tag de l'image dans docker-compose.yml, puis :
docker compose pull && docker compose up -dالأخطاء الشائعة وحلّها
الرمز 401 على كل طلب. المستخدم أو كلمة المرور لا يطابقان QDB_HTTP_USER وQDB_HTTP_PASSWORD. تحقّق من ملف .env ثم أعد تشغيل الحاوية بعد أي تعديل: لا تُقرأ المتغيرات إلا عند التشغيل.
الرمز 413 أثناء استيراد CSV. يتجاوز الملف حدّ nginx. ارفع client_max_body_size في المضيف الافتراضي وأعد تحميل nginx، أو قسّم الملف.
الحاوية تعيد التشغيل باستمرار. اقرأ docker compose logs questdb. السبب الأكثر شيوعًا هو الذاكرة: تعمل QuestDB على JVM ولا يكفي خادم بذاكرة 1 غيغابايت. انتقل إلى 2 غيغابايت على الأقل.
القرص يمتلئ. لا شيء ينتهي افتراضيًا: جدول حذف الأقسام القديمة وراقب حجم وحدة التخزين questdb-data بالأمر docker system df -v.
تطبيق على الخادم لا يتصل عبر PostgreSQL. استخدم 127.0.0.1:8812 والقاعدة qdb والمستخدم admin وكلمة المرور من .env: كلمة مرور المصنع quest مرفوضة.
تثبيت بنقرة واحدة من متجر ServOrbit
على خادم ServOrbit، يُثبَّت QuestDB من المتجر: ينشر السكربت الحاوية الرسمية، ويولّد كلمة مرور المستخدم admin، ويفعّل المصادقة على واجهة HTTP وبروتوكول PostgreSQL، ويوقف القياس عن بُعد، ويضبط nginx عبر HTTPS، على نطاق فرعي من نطاقك أو على النطاق الفرعي المجاني للخادم. تظهر كلمة المرور في مساحة العميل.