Django的多資料庫的處理(垂直分庫和水平分庫)

來源:互聯網
上載者:User

垂直分庫指的是根據應用來分資料庫,比如部落格一個資料庫,論壇一個資料庫。水平分庫是指,根據某些規則,將資料分布在不同的庫上。比如根據使用者ID把使用者的部落格文章分布在5個資料庫上。

垂直分庫,可以參考如下的文檔。
Easy Multi-Database Support for Django

這篇文章的主要思路是,繼承Manager建立一個MultiDBManager,在建立Model的時候用MultiDBManager替換預設的Manager,在MultiDBManager構造時指定資料庫。

核心的設計思路是,在外部設定settings的資料庫連接的值,在db內部使用。代碼如下,將settings的字典中的連結值設定到settings上。然後再調用資料庫連接。

for name, database in settings.DATABASES.iteritems():<br /> for key, value in database.iteritems():<br /> setattr(settings, key, value)<br />do something<br />

這樣作其實是很費勁的,因為django在設計之初就沒有考慮到multi-db的問題,connection都是直接讀取的setting的配置。

水平分庫,可以參考如下的討論連結,但是這傢伙只是給出了思路。
Proposal: user-friendly API for multi-database support

因為My Code很亂(夾雜其他功能),所以我也只給出思路和部分代碼。假設一個應用情境如下:使用者的某些訊息(msg),根據使用者的ID(數字)分布在2個資料庫中,則使用者ID對2求餘就可以了。
1.setting.py

DATABASE_ENGINE = 'mysql'<br />DATABASE_NAME = 'master'<br />DATABASE_USER = '001'<br />DATABASE_PASSWORD = ''<br />DATABASE_HOST = '127.0.0.1'<br />DATABASE_PORT = '3306'<br />DATABASES = dict(<br /> msg_0 = dict(<br /> DATABASE_NAME='msg0',<br /> ),<br /> msg_1 = dict(<br /> DATABASE_NAME='msg1',<br /> ),<br />)

2.類比一個settings.py出來,還有別的辦法,我用的笨辦法。
class DBSetting:<br /> def __init__(self,settings,db):<br /> self.DEBUG = False<br /> self.DATABASE_HOST = settings.DATABASE_HOST<br /> self.DATABASE_PORT = settings.DATABASE_PORT<br /> self.DATABASE_NAME = settings.DATABASE_NAME<br /> self.DATABASE_USER = settings.DATABASE_USER<br /> self.DATABASE_PASSWORD = settings.DATABASE_PASSWORD<br /> if db:<br /> dbdi = settings.DATABASES[db]<br /> if dbdi.has_key('DATABASE_HOST'):<br /> self.DATABASE_HOST = dbdi['DATABASE_HOST']<br /> if dbdi.has_key('DATABASE_PORT'):<br /> self.DATABASE_PORT = int(dbdi['DATABASE_PORT'])<br /> if dbdi.has_key('DATABASE_NAME'):<br /> self.DATABASE_NAME = dbdi['DATABASE_NAME']<br /> if dbdi.has_key('DATABASE_USER'):<br /> self.DATABASE_USER = dbdi['DATABASE_USER']<br /> if dbdi.has_key('DATABASE_PASSWORD'):<br /> self.DATABASE_PASSWORD = dbdi['DATABASE_PASSWORD']<br /> if dbdi.has_key('DATABASE_SEGMENT'):<br /> self.DATABASE_SEGMENT = dbdi['DATABASE_SEGMENT']

3.修改django安裝檔案下的django/db/backends/__init__.py檔案,改完要重新安裝。如果你直接修改/usr/lib/python2.5/site-packages/django/下的,則不需要。

class BaseDatabaseWrapper(local):<br /> #增加構造參數,把settings傳入<br /> def __init__(self,settings,**kwargs):<br /> self.connection = None<br /> self.queries = []<br /> self.options = kwargs<br /> self.settings = settings<br /> #...此處省略了代碼...<br /> def cursor(self):<br /> #把傳入的settings傳遞給子類<br /> cursor = self._cursor(self.settings)<br /> if self.settings.DEBUG:<br /> return self.make_debug_cursor(cursor)<br /> return cursor

4.好了,一個可以導入連結參數的connection就做好了。和第一個參考連結裡面的差不多,還是有點差異。處理資料庫連接,有兩個地方一個是QuerySet一個是insert

 

class MultiManager(models.Manager):<br /> #group指的是垂直分庫的標識,比如blog,bbs之類的<br /> def __init__(self, group, *args, **kwargs):<br /> self.group = group<br /> super(MultiManager, self).__init__(*args, **kwargs)<br />   #segment指的是用blog之下的哪個庫<br />   #比如settings.py裡面的blog_0,blog_1,那麼segment就可能是0和1<br /> def choiceConn(self,segment):<br /> self.segment = segment<br /> def _getConn(self):<br /> if self.group:<br /> if self.segment:<br /> key = self.group+'_'+str(self.segment)<br /> else:<br /> key = self.group+‘_0’ #預設連第一個<br /> conn = connPool.getConn(key)<br /> return conn<br /> else:<br /> return None<br /> def get_query_set(self):<br /> conn = self._getConn()<br /> if conn:<br /> query = sql.Query(self.model,conn)<br /> queryset = QuerySet(self.model,query)<br /> else:<br /> queryset = super(MultiManager, self).get_query_set()<br /> return queryset<br /> def _insert(self, values, return_id=False, raw_values=False):<br /> conn = self._getConn()<br /> if conn:<br /> query = sql.InsertQuery(self.model, conn)<br /> query.insert_values(values, raw_values)<br /> ret = query.execute_sql(return_id)<br /> query.connection._commit()

5.models的定義,也需要額外的處理,一般情況下的調用如:userMsg.objects.filter()/get()之類的,如果是水平分庫,則需要指定串連到哪個資料庫。因此,我定義了一個objects(seg)的方法,可以傳入資料編號。同時,儲存的時候,一般是這樣的:usermsg.save(),如果要分庫,則改為usermsg.save(seg)就可以了。

 

class userMsg(models.Model):<br /> msgid = models.AutoField(primary_key = True)<br /> userid = models.IntegerField()<br /> message = models.CharField(max_length = 128)<br /> _default_manager = MultiManager('msg')</p><p> @staticmethod<br /> def objects(seg):<br /> userMsg._default_manager.choiceConn(seg)<br /> return userMsg._default_manager<br /> def save(self,seg):<br /> userMsg._default_manager.choiceConn(seg)<br /> super(userMsg, self).save()

 

6.views中的使用,和django的使用沒有太大區別,就上面的兩個方法的差異。

 

經一步的思考:

1.資料庫事務的處理。在多資料庫的情況下,針對不同的資料庫,是無法使用事務的。因此,在架構上需要考慮異常和補償性的事務。在同一個資料庫上事務的處理,需要改寫transication.py,或者擷取當前connection,再進行事務的處理。

 

2.擴容的問題。增加資料庫後,會導致資料重新分配,那麼就涉及到資料移轉的問題,一個辦法是分布規則,考慮到擴容的問題,新的規則相容老的規則,舊有的資料不會變化。另外一種就是,可以平滑的在庫之間移動資料。這兩個都是很麻煩的問題。

 

==========================================================

 

ID規則,請看我的另外一篇文章

資料庫水平分布的ID規則問題

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.