Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a cikk útmutatást nyújt a Django-alkalmazások teljesítményének optimalizálásához a mssql-django háttérrendszer SQL Server való használatakor.
Kapcsolatoptimalizálás
Csökkentse a kapcsolat többletterhelését a készletezési, adatmegőrzési és időtúllépési beállítások finomhangolásával.
Kapcsolatkészletezés engedélyezése
A kapcsolatkészletezés alapértelmezés szerint engedélyezve van. Ellenőrizze, hogy nincs-e letiltva a(z) settings.py beállításaiban:
# Keep this True (or omit it entirely) for best connection performance
DATABASE_CONNECTION_POOLING = True
CONN_MAX_AGE használata
Állítsa be CONN_MAX_AGE , hogy az adatbázis-kapcsolatok nyitva maradjanak a kérések között, elkerülve az új kapcsolatok létrehozásának többletterhelését az egyes kérésekhez:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"USER": "<your-username>",
"PASSWORD": "<your-password>",
"HOST": "<your-server>",
"PORT": "1433",
"CONN_MAX_AGE": 600, # Keep connections open for 10 minutes
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
},
},
}
Lekérdezési időkorlát beállítása
Megakadályozza, hogy a hosszan futó lekérdezések határozatlan ideig használják fel az erőforrásokat:
DATABASES = {
"default": {
"ENGINE": "mssql",
"NAME": "<your-database>",
"USER": "<your-username>",
"PASSWORD": "<your-password>",
"HOST": "<your-server>",
"PORT": "1433",
"OPTIONS": {
"driver": "ODBC Driver 18 for SQL Server",
"query_timeout": 30,
},
},
}
Lekérdezésoptimalizálás
Ezekkel az ORM-technikákkal csökkentheti az adatbázis-körutakat és a lekérdezések számát.
N+1 lekérdezési minták elkerülése
Idegenkulcs-kapcsolatok esetén használja a select_related elemet (egyetlen JOIN lekérdezéshez), a több-többhöz vagy fordított kapcsolatok esetén pedig a prefetch_related elemet (külön lekérdezés IN záradékkal):
# Bad: N+1 queries
orders = Order.objects.all()
for order in orders:
print(order.customer.name) # Each access triggers a query
# Good: Single JOIN query
orders = Order.objects.select_related("customer").all()
for order in orders:
print(order.customer.name) # No additional queries
# Good: Two queries instead of N+1
orders = Order.objects.prefetch_related("items").all()
for order in orders:
for item in order.items.all(): # Uses prefetched data
print(item.name)
Az only() és a defer() használata
Korlátozza a lekért oszlopokat, ha nincs szüksége az összes mezőre:
# Retrieve only specific fields
products = Product.objects.only("name", "price").all()
# Defer loading of large fields
products = Product.objects.defer("description", "metadata").all()
Értékek() és values_list() használata
Ha nincs szüksége modellpéldányokra, könnyebb lekérdezésekhez használja a values() vagy values_list() elemet:
# Returns dictionaries instead of model instances
prices = Product.objects.values("name", "price")
# Returns tuples
names = Product.objects.values_list("name", flat=True)
Munka a 2100 paraméterkorláton belül
SQL Server az egyes lekérdezéseket 2100 paraméterre korlátozza. A Django paraméteres lekérdezéseket hoz létre, így a nagy IN záradékokat vagy tömeges értéklistákat létrehozó műveletek elérik ezt a korlátot.
Nagy IN záradékok automatikus optimalizálása:
Ha egy filter(field__in=list) hívás több mint 2048 értéket tartalmaz, a mssql-django háttérrendszer automatikusan beszúrja az értékeket egy ideiglenes táblába (1000-es kötegekben), és újraírja a lekérdezést WHERE field IN (SELECT params FROM #Temp_params). Ez az optimalizálás kódmódosítás nélkül elkerüli a paraméterkorlátot. Ez az összes __in lekérdezésre vonatkozik, beleértve a prefetch_related() által generáltakat is. A 2048-at a háttérrendszer úgy állítja max_in_list_size() be, hogy biztonságosan maradjon SQL Server 2100 paraméterkorlátja alatt.
Ennek az átírásnak ára van: a #Temp_params létrehozása és feltöltése további hálózati fordulókat és tempdb-tevékenységet eredményez. A küszöbértékhez közeli listák esetében mérje fel a számítási feladat mindkét megközelítését.
Ha még szükség van manuális beavatkozásra:
Az automatikus temp-table optimalizálás kezeli a(z) __in kereséseket, de ezek a műveletek továbbra is beleütközhetnek a 2 100 paraméteres korlátba, mert minden mezőérték külön paraméter:
-
bulk_create()vagybulk_update()sok objektummal és számos mezővel - Összetett
Q()kifejezések számos láncolt feltétellel - Olyan esetek, amikor el szeretné kerülni a feltöltéshez
#Temp_paramsszükséges ciklikus utakat (például ha egy kisebb lista és egy normálIN (...)gyorsabb lenne)
Megoldások:
Használja a(z)
batch_sizeelemet tömeges műveleteknél, hogy minden köteg a korláton belül maradjon:# Backend cap with 10 fields: min(1000, 2050 // 10 // 2) = 102 rows per batch # The backend applies the conservative // 2 divisor for both bulk_create and bulk_update. Product.objects.bulk_create(products, batch_size=100)Nagy adattömb-lekérdezések
IN, ha meg szeretné kerülni az automatikus temp-table mechanizmust:from itertools import islice def chunked_filter(queryset, field, values, chunk_size=2000): """Filter a queryset in chunks to stay within the 2,100 parameter limit.""" results = [] it = iter(values) while chunk := list(islice(it, chunk_size)): results.extend(queryset.filter(**{f"{field}__in": chunk})) return results # Returns a list of model instances, not a QuerySet products = chunked_filter(Product.objects, "pk", large_id_list)Használjon allekérdezéseket az azonosítólisták materializálása helyett:
# Instead of: Order.objects.filter(product_id__in=list(Product.objects.values_list("id", flat=True))) # Use a subquery (Django generates a single SQL statement with no parameter explosion) Order.objects.filter(product__in=Product.objects.filter(active=True))Használja a
Prefetchelemet szűrt lekérdezéshalmazokkal aprefetch_related()számára átadott azonosítók számának korlátozásához:from django.db.models import Prefetch orders = Order.objects.prefetch_related( Prefetch("items", queryset=OrderItem.objects.select_related("product")) )[:500] # Limit parent queryset size
Tömeges műveletek
Tömeges műveletek használata az adatbázis-körutak számának csökkentéséhez:
from decimal import Decimal
from myapp.models import Product
# Bulk create
new_products = [Product(name=f"Item {i}", price=Decimal("1.99") * i) for i in range(1000)]
Product.objects.bulk_create(new_products, batch_size=500)
# Bulk update: refetch so each instance has a primary key
products = list(Product.objects.filter(name__startswith="Item "))
for product in products:
product.price *= Decimal("1.10")
Product.objects.bulk_update(products, ["price"], batch_size=500)
Important
A(z) bulk_create vagy bulk_update használatakor állítsa be a(z) batch_size értékét az objektumonkénti mezők száma alapján. A háttérrendszer bulk_batch_size() az egyes kötegek méretét 1 000 sorban maximalizálja, és konzervatív 2050 / (fields * 2) paraméterkorlátot alkalmaz mindkettőrebulk_create és bulk_update. A további / 2 a bulk_update által mezőnként használt két paraméter számára van fenntartva (egy a CASE-illesztéshez, egy az értékhez), és ugyanez az osztó vonatkozik a bulk_create elemre is, így ugyanaz a kódútvonal mindkét művelethez biztonságosan használható.
Ha kihagyja batch_size, a háttérrendszer automatikusan kiszámít egy biztonságos értéket. Megadhat egy batch_size értéket is, és a háttérrendszer ezt tovább korlátozza a biztonságos határértékre.
A return_rows_bulk_insert és default paraméterekkel kapcsolatos további információkért lásd: Tömeges műveletek az mssql-django használatával.
Indexelési stratégiák
A Django automatikusan létrehozza az indexeket a ForeignKey, OneToOneFieldés a mezőkhöz.db_index=True További indexek esetén használja a következőt Meta.indexes:
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
indexes = [
models.Index(fields=["category", "price"]),
models.Index(fields=["-created_at"]),
]
Az SQL Server-specifikus indexek (például oszlopokat tartalmazó INCLUDE indexek) esetében használja a nyers SQL-t a migrálásokban:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("myapp", "0001_initial")]
operations = [
migrations.RunSQL(
sql="CREATE INDEX IX_product_category ON myapp_product (category) INCLUDE (name, price);",
reverse_sql="DROP INDEX IX_product_category ON myapp_product;",
),
]
A mssql-django háttérprogram támogatja a lefedő indexeket (supports_covering_indexes = True a következőben: mssql/features.py). A (3.2 és újabb verziók) által mssql-django támogatott összes Django-verzión használhatja a paramétert include a models.Index nyers SQL helyett:
class Product(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
class Meta:
indexes = [
models.Index(fields=["category"], include=["name", "price"], name="ix_product_cat_cover"),
]
Fájlcsoport elhelyezése
A mssql-django backend a Django db_tablespace elemét az SQL Server ON filegroup záradékának felelteti meg. Ezzel táblákat vagy indexeket helyezhet el adott fájlcsoportokon:
class LargeAuditLog(models.Model):
timestamp = models.DateTimeField(auto_now_add=True)
message = models.TextField()
class Meta:
db_tablespace = "ARCHIVE_FG"
Ez a következőt hozza létre: CREATE TABLE ... ON [ARCHIVE_FG].
Important
A fájlcsoportnak már léteznie kell a SQL Server adatbázisban a futtatás migrateelőtt. Hozza létre a(z) ALTER DATABASE [<your-database>] ADD FILEGROUP [ARCHIVE_FG] használatával, és adjon hozzá legalább egy fájlt.
Ablakfunkciók
A háttérrendszer támogatja SQL Server ablakfüggvényeit (supports_over_clause = True). Django-kifejezések Window használata rangsoroláshoz, összegek futtatásához és particionált számításokhoz:
from django.db.models import F, Window
from django.db.models.functions import Rank, RowNumber
# Rank products by price within each category
products = Product.objects.annotate(
price_rank=Window(
expression=Rank(),
partition_by=F("category"),
order_by=F("price").desc(),
)
)
# Row numbers across the full result set
products = Product.objects.annotate(
row_num=Window(
expression=RowNumber(),
order_by=F("created_at").asc(),
)
)
Note
SQL Server nem támogatjaNTH_VALUE(). Használja inkább a FIRST_VALUE, a LAST_VALUE vagy egy allekérdezéses kerülő megoldást. Lásd az mssql-django korlátozásait és nem támogatott funkcióit.
Lekérdezési teljesítmény figyelése
Használja a Django beépített lekérdezésnaplózását a lassú lekérdezések azonosításához a fejlesztés során:
LOGGING = {
"version": 1,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
},
"loggers": {
"django.db.backends": {
"level": "DEBUG",
"handlers": ["console"],
},
},
}
Előkészítési és éles számítási feladatokhoz használja SQL Server teljesítményeszközöket a Django által létrehozott SQL elemzéséhez:
A DMV-k közvetlen lekérdezése előtt kezdje a beépített teljesítményjelentésekkel.
- SQL Server és Azure SQL Managed Instance esetén használja a(z) Teljesítmény irányítópultot az SQL Server Management Studióban.
- Az Azure SQL Database esetében használja a Azure SQL Database lekérdezési teljesítményelemzését.
Ezek a jelentések általában a leggyorsabb módja annak, hogy drága lekérdezéseket, várakozásokat, blokkolást és erőforrás-terhelést találjanak, kevesebb hibalehetőséggel, mint az alkalmi DMV-lekérdezések.
A Query Store segítségével azonosíthatja a legtöbb erőforrást használó lekérdezéseket, valamint azokat a lekérdezéseket, amelyek teljesítménye nemrég romlott.
A SQL Server Management Studio leggyakoribb erőforrás-használó lekérdezései, regressziós lekérdezései és lekérdezési várakozási statisztikai nézetei alapján állapítsa meg, hogy a szűk keresztmetszet a PROCESSZOR, az I/O, a memória vagy a várakozás. Útmutatásért tekintse meg a számítási feladatok Query Store való monitorozásának ajánlott eljárásait.
Nyissa meg a lassú utasításhoz tartozó tényleges végrehajtási tervet, és keressen benne beolvasásokat, költséges kulcskereséseket, pontatlan sorbecsléseket és hiányzó indexeket.
Ha egy lekérdezés lassabb lett az üzembe helyezés vagy a séma módosítása után, hasonlítsa össze a terveit Query Store az alkalmazáskód módosítása előtt. Az adatbázis-adminisztrátor ideiglenesen kikényszeríthet egy ismerten jó végrehajtási tervet, amíg elhárítják az indexszel, a statisztikákkal vagy a lekérdezés felépítésével kapcsolatos problémát.
Ha a Query Store a magas CPU-idő helyett várakozásokat mutat, használja a Szűk keresztmetszetek azonosítása funkciót a processzor-, memória-, lemez-I/O-, kapcsolatterhelési és blokkolási problémák elkülönítésére.
Kapcsolódó tartalom
- Tömeges műveletek az mssql-django használatával
- Kapcsolatkészletezés az mssql-django-ban
- Újrapróbálkozási logika és kapcsolati rugalmasság az mssql-django használatával
- mssql-django konfigurációs referencia
- Teljesítmény irányítópult
- Lekérdezési teljesítményelemzés az Azure SQL Database-hez
- A teljesítmény figyelése a Query Store használatával
- Tényleges végrehajtási terv elemzése
- Django-adatbázis optimalizálása