(轉)Django配置數據庫讀寫分離

轉:https://blog.csdn.net/Ayhan_huang/article/details/78784486html

轉:http://www.cnblogs.com/dreamer-fish/p/5469141.htmlpython

目錄 

配置數據庫mysql

建立models並執行數據庫遷移git

讀寫分離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'),

建立models並執行數據庫遷移

這裏我簡單建立一張產品表

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'

配置Router

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,在此就須要分別指定。注意:這個是有順序的(先匹配上的規則,就先生效)
settings.py
# -*- 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
router.py
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'] 
models.py(app01)
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'] 
models.py(app02)

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
settings.py
# -*- coding:utf-8 -*-


class Router:
    def db_for_read(self, model, **hints):
        return 'slave'

    def db_for_write(self, model, **hints):
        return 'default'
utils/myrouter.py
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")
models.py
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/")
views.py
<!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>
index.html

 

相關文章
相關標籤/搜索