Django-Multitenant,分佈式多租戶數據庫項目實戰(Python/Django+Postgres+Citus)
- 2022 年 3 月 19 日
- 筆記
Python/Django
支持分佈式多租戶數據庫,如 Postgres+Citus
。
通過將租戶上下文添加到您的查詢來實現輕鬆橫向擴展,使數據庫(例如 Citus
)能夠有效地將查詢路由到正確的數據庫節點。
構建多租戶數據庫的架構包括:為每個租戶創建一個數據庫、為每個租戶創建一個 schema
和讓所有租戶共享同一個表。這個庫基於第三種設計,即讓所有租戶共享同一個表,它假設所有租戶相關的模型/表
都有一個 tenant_id
列來表示租戶。
以下鏈接更多地討論了何時以及如何為您的多租戶數據庫選擇正確架構的權衡:
關於多租戶的其他有用鏈接:
- //www.citusdata.com/blog/2017/03/09/multi-tenant-sharding-tutorial/
- //www.citusdata.com/blog/2017/06/02/scaling-complex-sql-transactions/
項目源碼
//github.com/citusdata/django-multitenant
安裝
pip install --no-cache-dir django_multitenant
支持的 Django 版本/前提條件。
Python | Django |
---|---|
3.X | 2.2 |
3.X | 3.2 |
3.X | 4.0 |
用法
為了使用這個庫,您可以使用 Mixins
或讓您的模型從我們的自定義模型類繼承。
模型變化
- 在要使用庫的任何文件中導入它:
from django_multitenant.fields import * from django_multitenant.models import *
- 所有模型都應繼承
TenantModel
類。Ex: class Product(TenantModel):
- 定義一個名為
tenant_id
的靜態變量,並使用該變量指定租戶列。Ex: tenant_id='store_id'
TenantModel
子類的所有外鍵都應使用TenantForeignKey
代替models.ForeignKey
- 實現上述
2
個步驟的示例模型:class Store(TenantModel): tenant_id = 'id' name = models.CharField(max_length=50) address = models.CharField(max_length=255) email = models.CharField(max_length=50) class Product(TenantModel): store = models.ForeignKey(Store) tenant_id='store_id' name = models.CharField(max_length=255) description = models.TextField() class Meta(object): unique_together = ["id", "store"] class Purchase(TenantModel): store = models.ForeignKey(Store) tenant_id='store_id' product_purchased = TenantForeignKey(Product)
使用 mixins 更改模型
- 在您要使用庫的任何文件中,只需:
from django_multitenant.mixins import *
- 所有模型都應使用
TenantModelMixin
和 djangomodels.Model
或您的客戶模型類Ex: class Product(TenantModelMixin, models.Model):
- 定義一個名為
tenant_id
的靜態變量,並使用該變量指定租戶列。Ex: tenant_id='store_id'
TenantModel
子類的所有外鍵都應使用TenantForeignKey
代替models.ForeignKey
- 實現上述 2 個步驟的示例模型:
class ProductManager(TenantManagerMixin, models.Manager): pass class Product(TenantModelMixin, models.Model): store = models.ForeignKey(Store) tenant_id='store_id' name = models.CharField(max_length=255) description = models.TextField() objects = ProductManager() class Meta(object): unique_together = ["id", "store"] class PurchaseManager(TenantManagerMixin, models.Manager): pass class Purchase(TenantModelMixin, models.Model): store = models.ForeignKey(Store) tenant_id='store_id' product_purchased = TenantForeignKey(Product) objects = PurchaseManager()
在 db
層自動化複合外鍵:
- 使用
TenantForeignKey
在租戶相關模型之間創建外鍵將自動將tenant_id
添加到引用查詢(例如product.purchases
)和連接查詢(例如product__name
)。如果要確保在db
層創建複合外鍵(帶有tenant_id
),則應將settings.py
中的數據庫ENGINE
更改為django_multitenant.backends.postgresql
。'default': { 'ENGINE': 'django_multitenant.backends.postgresql', ...... ...... ...... }
在哪裡設置租戶?
-
使用中間件編寫身份驗證邏輯,該中間件還為每個
session/request
設置/取消設置租戶。 這樣,開發人員不必擔心基於每個視圖設置租戶。只需在身份驗證時設置它,庫將確保其餘部分(將tenant_id
過濾器添加到查詢中)。上面的示例實現如下:from django_multitenant.utils import set_current_tenant class MultitenantMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.user and not request.user.is_anonymous: set_current_tenant(request.user.employee.company) return self.get_response(request)
在您的設置中,您需要更新
MIDDLEWARE
設置以包含您創建的設置。MIDDLEWARE = [ # ... # existing items # ... 'appname.middleware.MultitenantMiddleware' ]
-
在您希望基於租戶範圍的所有視圖中使用
set_current_tenant(t)
api 設置租戶。 這將自動(不指定顯式過濾器)將所有django API
調用範圍限定為單個租戶。如果未設置current_tenant
,則使用沒有租戶範圍的默認/原生
API。
支持的 API
Model.objects.*
下的大部分API
。Model.save()
為租戶繼承的模型注入tenant_id
。
s=Store.objects.all()[0]
set_current_tenant(s)
#All the below API calls would add suitable tenant filters.
#Simple get_queryset()
Product.objects.get_queryset()
#Simple join
Purchase.objects.filter(id=1).filter(store__name='The Awesome Store').filter(product__description='All products are awesome')
#Update
Purchase.objects.filter(id=1).update(id=1)
#Save
p=Product(8,1,'Awesome Shoe','These shoes are awesome')
p.save()
#Simple aggregates
Product.objects.count()
Product.objects.filter(store__name='The Awesome Store').count()
#Subqueries
Product.objects.filter(name='Awesome Shoe');
Purchase.objects.filter(product__in=p);
更多
- Django-Multitenant,分佈式多租戶數據庫項目實戰(Python/Django+Postgres+Citus)
- 分佈式 PostgreSQL 集群(Citus)官方示例 – 時間序列數據