
SQLite VFS مع استعلامات JOIN باردة من S3 بأقل من 100 مللي ثانية + ضغط وتشفير على مستوى الصفحات
turbolite هي VFS (نظام ملفات افتراضي) لـ SQLite مكتوبة بلغة Rust تقدم عمليات بحث نقطية و JOIN مباشرة من S3 بزمن استجابة بارد أقل من 250 مللي ثانية.
هذا المستودع هو مساحة عمل Cargo تحتوي على حزمتين:
turbolite — مكتبة Rust خالصة. VFS لـ SQLite مع ضغط على مستوى الصفحات، تشفير، وتصنيف إلى S3.turbolite-ffi — واجهة C FFI / إضافة قابلة للتحميل + روابط لغوية (Python، Node.js، Go).كما تقدم ضغطًا على مستوى الصفحات (zstd) وتشفيرًا (AES-256) للكفاءة والأمان أثناء التخزين، ويمكن استخدامها بشكل منفصل عن S3.
تجريبي. turbolite قيد التطوير النشط ويحتوي على أخطاء. كن حذرًا.
تخزين الكائنات أصبح سريعًا. S3 Express One Zone يوفر عمليات GET بزمن استجابة أحادي الرقم بالميلي ثانية و Tigris سريع للغاية أيضًا. الفجوة بين القرص المحلي والتخزين السحابي تتقلص، و turbolite تستغل ذلك.
التصميم والاسم مستوحى من نهج turbopuffer في البناء بصرامة حول قيود التخزين السحابي. الهدف الأولي للمشروع كان التغلب على بدء التشغيل البارد الذي يتجاوز 500 مللي ثانية لـ Neon. الهدف تحقق.
إذا كان لديك قاعدة بيانات واحدة لكل خادم، استخدم وحدة تخزين. turbolite تستكشف كيفية امتلاك مئات أو آلاف قواعد البيانات (واحدة لكل مستأجر، واحدة لكل مساحة عمل، واحدة لكل جهاز)، ولا ترغب في وحدة تخزين لكل منها، وتقبل وجود مصدر كتابة واحد.
يتم شحن turbolite كمكتبة Rust، و إضافة قابلة للتحميل لـ SQLite (.so/.dylib)، وحزم لغات لـ Python و Node.js، بالإضافة إلى تبعيات Github لـ Go. أي تخزين متوافق مع S3 يعمل (AWS S3، Tigris، R2، MinIO، إلخ). إنها VFS قياسية لـ SQLite تعمل على مستوى الصفحات، لذا يجب أن تعمل معظم ميزات SQLite: FTS، R-tree، JSON، وضع WAL، إلخ.
turbolite جزء من النظام البيئي الأوسع hadb. turbolite المستقلة هي VFS تخزين مع كاتب آمن واحد؛ إذا كنت تريد انتخاب القائد عالي التوفر بالإضافة إلى نسخ WAL المستمر، استخدمها من خلال haqlite-turbolite، التي تضيف HaQLite و walrust فوقها. هذا المسار عالي التوفر لا يزال تجريبيًا جدًا.
إذا كنت ترغب في المساهمة في turbolite أو العثور على أخطاء، يرجى إنشاء طلب سحب أو فتح مشكلة.
| الاستعلام | النوع | بارد (S3 Express) | بارد (Tigris) |
|---|---|---|---|
| منشور + مستخدم | بحث نقطي + JOIN | 86ms | 172ms |
| الملف الشخصي | JOIN متعدد الجداول (5 JOIN) | 251ms | 479ms |
| من أعجب | بحث فهرس + JOIN | 206ms | 302ms |
| الأصدقاء المشتركون | بحث متعدد + JOIN | 19ms | 49ms |
| تصفية مفهرسة | مسح فهرس مغطى | 79ms | 88ms |
| مسح كامل + تصفية | مسح جدول كامل | 476ms | 532ms |
1M منشور / 100K مستخدم (~1.5GB مخزن) مع عدم تخزين أي شيء مؤقتًا، كل بايت من S3. EC2 c5.2xlarge + S3 Express One Zone (نفس منطقة التوفر، ~4ms زمن استجابة GET). Fly performance-8x + Tigris (~25ms زمن استجابة GET). كلاهما: 8 vCPU مخصصة، 16GB RAM، 7 خيوط عمل جلب مسبق. انظر قياس الأداء و مهمة الواجهة الخلفية للتخزين.
يتم تنظيم المقاييس حسب مستوى التخزين المؤقت (ما هو موجود بالفعل على القرص المحلي عند تشغيل الاستعلام):
| مستوى التخزين المؤقت | ما هو مخبأ | ما يتم جلبه من S3 | متى يحدث هذا |
|---|---|---|---|
| لا شيء | لا شيء | كل شيء | بداية جديدة، ذاكرة تخزين مؤقت فارغة |
| داخلي | صفحات B-tree الداخلية | صفحات الفهرس + البيانات | أول استعلام بعد فتح الاتصال |
| فهرس | الصفحات الداخلية + صفحات الفهرس | صفحات البيانات فقط | عملية turbolite العادية |
| بيانات | كل شيء | لا شيء | مكافئ لـ SQLite المحلي |
الداخلي هو المعيار الأكثر واقعية للبرودة: يتم تحميل الصفحات الداخلية بفارغ الصبر عند فتح الاتصال، لذا بحلول وقت تشغيل أول استعلام، تكون مخبأة. يتم جلب صفحات الفهرس بقوة في الخلفية عند أول وصول وقد لا تكون جاهزة بعد.
100K صف، Fly.io performance-2x (vCPU مخصصة، NVMe، IAD):
| العملية | SQLite | turbolite | النفقات العامة |
|---|---|---|---|
| بحث نقطي | 145K/s | 73K/s | 2.0x |
| مسح نطاقي | 8.8K/s | 8.3K/s | تعادل |
| مسح جدول كامل | 56/s | 60/s | تعادل |
| INSERT | 19K/s | 23K/s | تعادل |
| UPDATE حسب المفتاح الأساسي | 40K/s | 27K/s | 1.5x |
| INSERT دفعي (في معاملة) | 685K/s | 740K/s | تعادل |
عمليات البحث النقطي لديها أعلى نفقات عامة لكل صفحة (~2x). كل شيء آخر يحقق التعادل أو يتجاوزه. بنية التخزين المؤقت الخالية من الأقفال تعني أن القراءات المتزامنة لا تمنع الكتابة أبدًا.
| بعد | محلي | S3 (RustFS في نفس المنطقة) |
|---|---|---|
| 1K إدخالات | 19ms | 38ms |
| 10K دفعة | 17ms | 114ms |
| 1K تحديثات | 9ms | 36ms |
الكتابة دائمًا بسرعة محلية. تكلفة S3 تكون فقط عند نقطة التفتيش. الأرقام مع RustFS في نفس منطقة Fly (~2ms RTT). S3 Express One Zone سيكون مشابهًا.
pip install turbolite
curl -X POST http://127.0.0.1:5000/update
-H "Content-Type: application/x-www-form-urlencoded"
-d 'username=test&password=test&new_password=newpassword'
سيقوم هذا الأمر بتعيين كلمة مرور جديدة للمستخدم `test`.
## الميزات
- **مصادقة المستخدم**: تسجيل دخول آمن باستخدام اسم المستخدم وكلمة المرور.
- **تحديث كلمة المرور**: تغيير كلمة المرور للمستخدمين الموثَّقين.
- **استرجاع العَلَمة**: الحصول على العَلَمة (flag) للمستخدم المسؤول.
- **ميزات الأمان**:
- تحديد معدل المحاولات لتسجيل الدخول.
- التحقق من صحة الإدخال لجميع الحقول.
- إدارة الجلسات باستخدام رموز JWT.
- تجزئة كلمة المرور باستخدام bcrypt.
## التثبيت
1. استنساخ المستودع:
```bash
git clone https://github.com/example/repo.git
cd repo
pip install -r requirements.txt
python app.py
``````python
import turbolite
conn = turbolite.connect("my.db", mode="s3", bucket="my-bucket", endpoint="https://t3.storage.dev")
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)") conn.execute("INSERT INTO users VALUES (1, 'alice', '[email protected]')") conn.commit()
alice = conn.cursor().execute("SELECT * FROM users").fetchone() print(alice[1])
"alice"
انظر [التثبيت](#installation) لـ Node، Go، Rust، الوضع المحلي فقط، واستخدام الامتداد القابل للتحميل `.so` مباشرة
## التصميم
صُممت turbolite لقيود S3 بدلاً من قيود نظام الملفات. كل قرار ينبع من هذا النموذج:
| قيد S3 | التأثير |
|--------|---------|
| **رحلة الذهاب والإياب بطيئة** | قلل عدد الطلبات. اكتب بشكل مجمع، اقرأ مسبقاً بشكل هجومي. |
| **عرض النطاق هو عنق الزجاجة** | عزز استخدام عرض النطاق الترددي إلى أقصى حد. |
| **عمليات PUT و GET تُفرض رسوم لكل عملية** | تكلفة GET بحجم 64KB نفس تكلفة GET بحجم 16MB. حسّن عدد الطلبات، وليس كفاءة البايت. |
| **الكائنات غير قابلة للتعديل** | لا تقم بالتحديث في المكان. اكتب إصدارات جديدة، بدّل المؤشر. لا تلف ناتج عن الكتابة الجزئية. |
| **التخزين رخيص** | لا تحسّن للمساحة. وفّر في التجهيز، احتفظ بالإصدارات القديمة، واترك لمجمع القمامة التنظيف لاحقاً. |
### الهندسة المعمارية
تضيف turbolite طبقات من الفحص الداخلي والإعادة التوجيه بين SQLite و S3 تقوم بتجميع الصفحات وضغطها وتتبعها وجلبها بكفاءة.
يستخدم SQLite فهرس B-tree ويطلب صفحة واحدة في كل مرة. يعلم أن الصفحة N تقع عند الإزاحة البايتية `N * page_size`. وهذه الصفحات موزعة عشوائياً على خريطة الصفحات للوصول العشوائي الفعال. لكن على S3، جلب صفحة واحدة لكل طلب يعني آلاف عمليات GET العشوائية المحتملة لكل استعلام.
لكن الصفحات ليست متساوية. يحتوي SQLite على أنواع مختلفة من الصفحات. تفصل turbolite **مجموعات الصفحات حسب النوع**: الصفحات الداخلية لـ B-tree، وصفحات أوراق الفهرس، وصفحات أوراق البيانات.
يتم الوصول إلى الصفحات الداخلية في كل استعلام لتوجيه عمليات البحث إلى صفحات الأوراق. تكتشفها turbolite، وتخزنها في حزم مضغوطة في S3، وتحمّلها بشغف عند فتح VFS. بعد ذلك، كل مسار B-tree يصبح ضربة مخبأ.