轉:https://blog.csdn.net/Ayhan_huang/article/details/78784486html
轉:http://www.cnblogs.com/dreamer-fish/p/5469141.htmlpython
配置數據庫mysql
讀寫分離web
手動讀寫分離sql
自動讀寫分離數據庫
配置Routerdjango
一主多從方案app
分庫分表運維
對網站的數據庫做讀寫分離(Read/Write Splitting)能夠提升性能,在Django中對此提供了支持,下面咱們來簡單看一下。注意,還須要運維人員做數據庫的讀寫分離和數據同步。
咱們知道在Django項目的settings中,能夠配置數據庫,除了默認的數據庫,我在下面又加了一個db2。由於是演示,我這裏用的是默認的SQLite,若是但願用MySQL,看這裏 。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'db2': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'),
這裏我簡單建立一張產品表
from django.db import models class Products(models.Model): """產品表""" prod_name = models.CharField(max_length=30) prod_price = models.DecimalField(max_digits=6, decimal_places=2)
建立完成後,執行數據庫遷移操做:
python manage.py makemigrations # 在migrations文件夾下生成記錄,遷移前檢查 python manage.py migrate # 建立表 # python manage.py migrate --database default python manage.py migrate --database db2 # 根據settings裏的名稱逐個遷移
在migrations文件夾下生成記錄,並在遷移前檢查是否有問題,默認值檢查defualt數據庫,可是能夠在後面的數據庫路由類(Router)中經過allow_migrate()方法來指定是否檢查其它的數據庫。
其實第二步遷移默認有參數python manage.py migrate --database default ,在默認數據庫上建立表。所以完成以上遷移後,執行python manage.py --database db2,再遷移一次,就能夠在db2上建立相同的表。這樣在項目根目錄下,就有了兩個表結構同樣的數據庫,分別是db.sqlite3和db2.sqlite3。
在使用數據庫時,經過.using(db_name)來手動指定要使用的數據庫
from django.shortcuts import HttpResponse from . import models def write(request): models.Products.objects.using('default').create(prod_name='熊貓公仔', prod_price=12.99) return HttpResponse('寫入成功') def read(request): obj = models.Products.objects.filter(id=1).using('db2').first() return HttpResponse(obj.prod_name)
經過配置數據庫路由,來自動實現,這樣就不須要每次讀寫都手動指定數據庫了。數據庫路由中提供了四個方法。這裏這裏主要用其中的兩個:def db_for_read()決定讀操做的數據庫,def db_for_write()決定寫操做的數據庫。
定義Router類
新建myrouter.py腳本,定義Router類:
class Router: def db_for_read(self, model, **hints): return 'db2' def db_for_write(self, model, **hints): return 'default'
settings.py中指定DATABASE_ROUTERS
DATABASE_ROUTERS = ['myrouter.Router',]
能夠指定多個數據庫路由,好比對於讀操做,Django將會循環全部路由中的db_for_read()方法,直到其中一個有返回值,而後使用這個數據庫進行當前操做。
網站的讀的性能一般更重要,所以,能夠多配置幾個數據庫,並在讀取時,隨機選取,好比:
class Router: def db_for_read(self, model, **hints): """ 讀取時隨機選擇一個數據庫 """ import random return random.choice(['db2', 'db3', 'db4']) def db_for_write(self, model, **hints): """ 寫入時選擇主庫 """ return 'default'
在大型web項目中,經常會建立多個app來處理不一樣的業務,若是但願實現app之間的數據庫分離,好比app01走數據庫db1,app02走數據庫
class Router: def db_for_read(self, model, **hints): if model._meta.app_label == 'app01': return 'db1' if model._meta.app_label == 'app02': return 'db2' def db_for_write(self, model, **hints): if model._meta.app_label == 'app01': return 'db1' if model._meta.app_label == 'app02': return 'db2'
別人的例子
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'testly', 'USER': 'root', 'PASSWORD': '123456789', 'HOST':'192.168.1.1', 'PORT':'3306', }, 'hvdb':{ #配置第二個數據庫節點名稱 'ENGINE': 'django.db.backends.mysql', 'NAME': 'testdjango', #第二個數據庫的名稱 'USER': 'root', 'PASSWORD': '123456789', 'HOST':'192.168.1.1', 'PORT':'3306', } } DATABASE_ROUTERS = ['tt.db_router.app02Router'] #tt爲當前項目名稱,db_router爲上一步編寫的db_router.py文件,app02Router爲Router #DATABASE_ROUTERS = ['tt.db_router.app02Router','tt.db_router.app01Router'] #若是定義了多個Router,在此就須要分別指定。注意:這個是有順序的(先匹配上的規則,就先生效)
# -*- coding: UTF-8 -*- class app02Router(object): #配置app02的路由,去鏈接hvdb數據庫 """ A router to control all database operations on models in the app02 application. """ def db_for_read(self, model, **hints): """ Attempts to read app02 models go to hvdb DB. """ if model._meta.app_label == 'app02': #app name(若是該app不存在,則沒法同步成功) return 'hvdb' #hvdb爲settings中配置的database節點名稱,並不是db name。dbname爲testdjango return None def db_for_write(self, model, **hints): """ Attempts to write app02 models go to hvdb DB. """ if model._meta.app_label == 'app02': return 'hvdb' return None def allow_relation(self, obj1, obj2, **hints): """ Allow relations if a model in the app02 app is involved. 當 obj1 和 obj2 之間容許有關係時返回 True ,不容許時返回 False ,或者沒有 意見時返回 None 。 """ if obj1._meta.app_label == 'app02' or \ obj2._meta.app_label == 'app02': return True return None def allow_migrate(self, db, model): """ Make sure the app02 app only appears in the hvdb database. """ if db == 'hvdb': return model._meta.app_label == 'app02' elif model._meta.app_label == 'app02': return False def allow_syncdb(self, db, model): #決定 model 是否能夠和 db 爲別名的數據庫同步 if db == 'hvdb' or model._meta.app_label == "app02": return False # we're not using syncdb on our hvdb database else: # but all other models/databases are fine return True return None # class app01Router(object): # """ # A router to control all database operations on models in the # aew application. # """ # def db_for_read(self, model, **hints): # """ # Attempts to read aew models go to aew DB. # """ # if model._meta.app_label == 'app01': # return 'default' # return None # def db_for_write(self, model, **hints): # """ # Attempts to write aew models go to aew DB. # """ # if model._meta.app_label == 'app01': # return 'default' # return None # def allow_relation(self, obj1, obj2, **hints): # """ # Allow relations if a model in the aew app is involved. # """ # if obj1._meta.app_label == 'app01' or obj2._meta.app_label == 'app01': # return True # return None # def allow_migrate(self, db, model): # """ # Make sure the aew app only appears in the aew database. # """ # if db == 'default': # return model._meta.app_label == 'app01' # elif model._meta.app_label == 'app01': # return False # return None
class tb05(models.Model): #該model使用default數據庫 name=models.CharField(max_length=100,primary_key=True,unique=True) ip=models.GenericIPAddressField() rating = models.IntegerField() def __str__(self): return self.name class Meta: #app_label = 'app01' #因爲該model鏈接default數據庫,因此在此無需指定 ordering = ['name'] class tb2(models.Model): name=models.CharField(max_length=100,primary_key=True,unique=True) ip=models.GenericIPAddressField() rating = models.IntegerField() def __str__(self): return self.name class Meta: app_label = 'app02' ordering = ['name']
class mtable01(models.Model): name=models.CharField(max_length=100,primary_key=True,unique=True) ip=models.GenericIPAddressField() rating = models.IntegerField() def __str__(self): return self.name class Meta: app_label = 'app02' #定義該model的app_label ordering = ['name'] 使用migrate命令同步數據庫: class tb06(models.Model): name=models.CharField(max_length=100,primary_key=True,unique=True,db_column='mycname') #使用db_column自定義字段名稱 ip=models.GenericIPAddressField() rating = models.IntegerField() def __str__(self): return self.name class Meta: db_table = 'mytable' #自定義表名稱爲mytable verbose_name = '自定義名稱' #指定在admin管理界面中顯示的名稱 app_label = 'app02' ordering = ['name']
migrate管理命令一次操做一個數據庫。默認狀況下,它在default 數據庫上操做,可是經過提供一個 --database 參數,告訴migrate同步一個不一樣的數據庫。
1)同步default節點數據庫,只運行不帶 --database參數的命令,不對其餘數據庫進行同步
python manage.py migrate
python manage.py makemigrations
python manage.py migrate
2)同步hvdb節點數據庫:
python manage.py migrate --database=hvdb
python manage.py makemigrations
python manage.py migrate --database=hvdb
結果:
testdjango數據庫(hvdb節點)下的app02_mtable01表對應app02下的mtable01模型
testdjango數據庫(hvdb節點)下的app02_tb2表對應app01下的tb2模型
testly數據庫(default節點)下的app01_tb05表對應app01下的tb05模型
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'review_scrapy_dj', 'USER': 'xxxx', 'PASSWORD': 'xxxx', 'HOST': '1.1.1.39', 'PORT': '3306', 'CHARSET':'utf8', "COLLATION":'utf8_general_ci' }, "slave":{ 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'), } } DATABASE_ROUTERS = ['utils.myrouter.Router',] # router
# -*- coding:utf-8 -*- class Router: def db_for_read(self, model, **hints): return 'slave' def db_for_write(self, model, **hints): return 'default'
from django.db import models # Create your models here. class UserInfo(models.Model): name = models.CharField(max_length=64) gender_choices = [ (0, "男"), (1, "女"), ] gender = models.IntegerField(choices=gender_choices,null=True,default=None) class Role(models.Model): role = models.CharField(max_length=64) user = models.ManyToManyField(to="UserInfo")
from django.shortcuts import render,HttpResponse,redirect from django.views import View from . import models # Create your views here. class Index(View): def get(self,request): user_objs_read = models.UserInfo.objects.all() user_objs_default = models.UserInfo.objects.using("default").all() gender_choices = models.UserInfo.gender_choices context = { "user_objs_read":user_objs_read, "gender_choices":gender_choices, "user_objs_default":user_objs_default, } return render(request,"index.html",context=context) def post(self,request): name = request.POST.get("name") print(type(request.POST.get("gender"))) gender = int(request.POST.get("gender")) models.UserInfo.objects.create(name=name,gender=gender) return redirect("/test2db/")
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>index</h1> <div> <h3>user_objs_read</h3> {% for user_obj in user_objs_read %} <div>{{ user_obj.name }}——{{ user_obj.get_gender_display }}</div> {% endfor %} </div> <hr> <div> <h3>user_objs_write</h3> {% for foo in user_objs_default %} <div>{{ foo.name }}——{{ foo.get_gender_display }}</div> {% endfor %} </div> <hr> <form action="" method="post"> {% csrf_token %} <div>name<input type="text" name="name"></div> <div>gender <select name="gender" id=""> {% for gender_choice in gender_choices %} <option value="{{ gender_choice.0 }}">{{ gender_choice.1 }}</option> {% endfor %} </select> </div> <input type="submit"> </form> </body> </html>