תארו לעצמכם שאתר המסחר האלקטרוני שלכם על RDS מתחיל להאט בזמני עומס, ואתם משלמים יותר מדי על משאבים לא מנוצלים. מסדי נתונים Serverless פותרים את זה—הם מתכווננים אוטומטית לאפס כשהם לא פעילים ומחויבים לפי צריכה. אבל עיצוב סכמה לא נכון יכול לבטל את החיסכון: לדוגמה, סריקות תכופות ב-DynamoDB יכולות לעלות יותר מתשתית קבועה. אנו מתקינים מסדי נתונים Serverless לפרויקטים בעומס גבוה: DynamoDB, PlanetScale ו-Neon. עם ניסיון של 5+ שנים בארכיטקטורות Serverless, יישמנו פתרונות ל-50+ פרויקטים—ממסחר אלקטרוני ועד IoT.
לפי תיעוד AWS, עיצוב Single-Table Design נכון מפחית פעולות קריאה בעד 90%.
למה לבחור במסד נתונים Serverless?
מסדי נתונים Serverless מבטלים את הצורך בניהול תשתית. אתם לא משלמים על משאבים לא פעילים, והקנה מידה מתבצע אוטומטית. לפרויקטים עם עומס לא אחיד, זה מפחית את עלויות התשתית ב-35–40%. עם זאת, עיצוב סכמה לא נכון יכול לבטל את כל היתרונות: לדוגמה, סריקות תכופות ב-DynamoDB יובילו להוצאות גבוהות.
השוואת אפשרויות
| DB | סוג | קנה מידה | תרחיש מומלץ |
|---|---|---|---|
| DynamoDB | מפתח-ערך / מסמך | בלתי מוגבל | עומס גבוה, שאילתות צפויות |
| PlanetScale | תואם MySQL | עד טרה-בייטים | נתונים יחסיים, סניפים בסגנון GitHub |
| Neon | PostgreSQL | קטן-בינוני | SQL מלא, סביבות פיתוח |
| FaunaDB | מסמך / יחסי | בינוני | עקביות רב-אזורית |
| Upstash | Redis | קטן-בינוני | מטמון, תורים, הגבלת קצב |
DynamoDB: עיצוב ל-Serverless
DynamoDB דורש חשיבה על גישת נתונים לפני עיצוב הסכמה. טבלה מעוצבת בצורה גרועה היא יקרה ואיטית. הגישה הנכונה היחידה היא Single-Table Design, שבו כל התחום מאוחסן בטבלה אחת, ו-PK/SK מקודדים את סוג הישות:
# Схема для e-commerce
# PK | SK | Данные
# USER#u123 | PROFILE | {name, email}
# USER#u123 | ORDER#o456 | {status, total, items}
# USER#u123 | ORDER#o789 | {status, total, items}
# ORDER#o456 | ITEM#i001 | {product_id, qty, price}
# PRODUCT#p001 | METADATA | {title, description}
import boto3
from boto3.dynamodb.conditions import Key
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('ecommerce')
def get_user_with_orders(user_id: str) -> dict:
response = table.query(
KeyConditionExpression=Key('PK').eq(f'USER#{user_id}') & Key('SK').begins_with('ORDER#')
)
return response['Items']
def put_order(user_id: str, order: dict):
with table.batch_writer() as batch:
batch.put_item(Item={
'PK': f'USER#{user_id}',
'SK': f'ORDER#{order["id"]}',
**order
})
batch.put_item(Item={
'PK': f'ORDER#{order["id"]}',
'SK': 'METADATA',
'GSI1PK': f'STATUS#{order["status"]}',
'GSI1SK': order['created_at'],
**order
})Global Secondary Index (GSI) — עבור דפוסי גישה חלופיים, לדוגמה, שליפת כל ההזמנות לפי סטטוס.
DynamoDB On-Demand לעומת Provisioned
On-Demand: תשלום לפי קריאה/כתיבה. אין צורך בתכנון קיבולת. אידיאלי לתנועה לא צפויה או לאפליקציות חדשות.
Provisioned + Auto Scaling: הגדרת קיבולת בסיסית של RCU/WCU, קנה מידה אוטומטי בזמני עומס. זול יותר בעומס צפוי.
resource "aws_dynamodb_table" "ecommerce" {
name = "ecommerce"
billing_mode = "PAY_PER_REQUEST" # On-demand
hash_key = "PK"
range_key = "SK"
attribute {
name = "PK"
type = "S"
}
attribute {
name = "SK"
type = "S"
}
attribute {
name = "GSI1PK"
type = "S"
}
attribute {
name = "GSI1SK"
type = "S"
}
global_secondary_index {
name = "GSI1"
hash_key = "GSI1PK"
range_key = "GSI1SK"
projection_type = "ALL"
}
ttl {
attribute_name = "expires_at"
enabled = true
}
} Neon: PostgreSQL Serverless
Neon מפריד בין מחשוב לאחסון. המחשוב מתכוונן לאפס כשאינו פעיל, והאחסון מחויב לפי נפח. סניפים מובנים מאפשרים יצירת סביבות מבודדות תוך שניות.
import psycopg2
import os
conn = psycopg2.connect(os.environ['DATABASE_URL'])
with conn.cursor() as cur:
cur.execute("SELECT * FROM orders WHERE user_id = %s", (user_id,))
orders = cur.fetchall()
סניפים ב-Neon:
neon branches create --name feature/new-schema --parent main
neon connection-string feature/new-schema # → postgresql://user:[email protected]/neondb PlanetScale
שירות תואם MySQL עם זרימת עבודה של סניפים, ללא אילוצי מפתח זר (מבוסס vitess). משתמש בפרוטוקול HTTP, טוב במיוחד ל-Lambda:
import { connect } from '@planetscale/database'
const conn = connect({
host: process.env.DATABASE_HOST,
username: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD
})
const results = await conn.execute(
'SELECT * FROM orders WHERE user_id = ?',
[userId]
) איך להגדיר Connection Pooling ל-Lambda: מדריך שלב-אחר-שלב
- קבעו את סוג ה-DB: עבור RDS השתמשו ב-RDS Proxy, עבור Neon השתמשו ב-pooler מובנה, עבור PlanetScale פרוטוקול ה-HTTP כבר פותר את הבעיה.
- עבור PostgreSQL עם PgBouncer: פרסו את PgBouncer ליד Lambda (לדוגמה, ב-VPC), הגדירו pool_mode = transaction.
- בשימוש ב-Prisma: החליפו חיבורים ישירים ב-Prisma Accelerate דרך משתנה סביבה
# Схема для e-commerce # PK | SK | Данные # USER#u123 | PROFILE | {name, email} # USER#u123 | ORDER#o456 | {status, total, items} # USER#u123 | ORDER#o789 | {status, total, items} # ORDER#o456 | ITEM#i001 | {product_id, qty, price} # PRODUCT#p001 | METADATA | {title, description} import boto3 from boto3.dynamodb.conditions import Key dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('ecommerce') def get_user_with_orders(user_id: str) -> dict: response = table.query( KeyConditionExpression=Key('PK').eq(f'USER#{user_id}') & Key('SK').begins_with('ORDER#') ) return response['Items'] def put_order(user_id: str, order: dict): with table.batch_writer() as batch: batch.put_item(Item={ 'PK': f'USER#{user_id}', 'SK': f'ORDER#{order["id"]}', **order }) batch.put_item(Item={ 'PK': f'ORDER#{order["id"]}', 'SK': 'METADATA', 'GSI1PK': f'STATUS#{order["status"]}', 'GSI1SK': order['created_at'], **order }). - בדקו את מספר החיבורים המקבילים: עבור 100 פונקציות Lambda, יידרשו כ-20 חיבורים ב-pool.
- בדקו Cold Start: הבקשה הראשונה לאחר חוסר פעילות לא תעלה על 300 אלפיות השנייה.
Connection Pooling ל-Lambda
Lambda יוצר חיבור חדש בכל Cold Start. עם 100 Lambda מקבילים—100 חיבורים. זה הורג את PostgreSQL/MySQL. פתרונות:
| פתרון | תיאור | מומלץ עבור |
|---|---|---|
| RDS Proxy (AWS) | Pooler חיבורים מול RDS, שקוף | אפליקציות על RDS |
| PgBouncer | מארח עצמי, מול PostgreSQL | כל PostgreSQL, כוונון עדין |
| Neon/PlanetScale | Pooling מובנה | שירותים מנוהלים, ללא הגדרות |
| Prisma Accelerate | Pooler + מטמון שאילתות | מחסנית על Prisma |
מקרה בוחן: העברת לקוח מ-RDS ל-Neon
פרויקט: חנות מסחר אלקטרוני עם עומסים עונתיים (Black Friday). מסד נתונים: PostgreSQL 13 על RDS. עומס ממוצע: 2000 בקשות/שנייה, שיא: 15,000. RDS לא עמד בזה, ודרש קיבולת מופרזת.
פתרון: מעבר ל-Neon עם PostgreSQL 15. תוצאות:
- עלות התשתית ירדה ב-35% (מ-$4k–5.8k ל-$2.6k–3.8k לחודש).
- Cold Start (לאחר חוסר פעילות) — 200 אלפיות השנייה לעומת 2 שניות קודם על RDS.
- זמן אינדוקס נתונים חדשים קטן פי שלושה בזכות פעולות מקבילות.
- הצוות קיבל יכולת ליצור סניפים לכל PR, מה שהאיץ את הפיתוח.
מה כלול בעבודה
- עיצוב סכמה (Single-Table Design ל-DynamoDB, נורמליזציה ל-Neon/PlanetScale).
- הגדרת Connection Pooling (RDS Proxy, PgBouncer, מובנה).
- אופטימיזציה של שאילתות (אינדקסים, GSI, בדיקות ביצועים).
- CI/CD לסניפים (יצירת סניף אוטומטית ב-Pull Requests).
- תיעוד סכמה והוראות לצוות.
- הכשרת מפתחים (המהנדסים שלכם יוכלו לבצע שינויים באופן עצמאי).
- אחריות ל-30 יום לאחר המסירה.
לוחות זמנים משוערים
- עיצוב טבלה יחידה ב-DynamoDB + פעולות בסיסיות — 3-5 ימים.
- התקנת Neon / PlanetScale + Pooling — 1-2 ימים.
- העברת מסד נתונים קיים ל-Serverless — 5-14 ימים.
לוחות הזמנים משתנים בהתאם למורכבות הסכמה ולנפח הנתונים. קבלו ייעוץ לפרויקט שלכם—צרו קשר להערכה. נבחר את הפתרון האופטימלי ונציע תוכנית עבודה.







