1. 程式人生 > >mysql 資料庫優化之 django分表方案

mysql 資料庫優化之 django分表方案

由來

這個問題戳到了Django ORM的痛點,對於多資料庫/分庫的問題,Django提供了很好的支援,通過using和db router可以很好的完成多資料庫的操作。但是說到分表的問題,就有點不那麼友好了。但也不是那麼難處理,只是處理起來不太優雅。

解析

在Django中,資料庫訪問的邏輯基本上是在Queryset中完成的,一個查詢請求,比如:User.objects.filter(group_id=10)

其中的objects其實就是models.Manager,而Manager又是對QuerySet的一個包裝。而QuerySet又是最終要轉換為sql的一箇中間層(就是ORM種,把Model操作轉換為SQL語句的部分)。所以當我們寫下User.objects

的時候,就已經確定了要訪問的是哪個表了,這是由class Meta中的db_table決定的。

  1. classUser(models.Model):
  2. username = models.CharField(max_length=255)
  3. classMeta:
  4. db_table ='user'

理論上講,我們可以通過在執行時修改db_table來完成分表CRUD的邏輯,但是the5fire在看了又看原始碼之後,還是沒找到如何下手。還是上面的問題,當執行到User.objects的時候,表已經確定了,當執行到User.objects.filter(group=10)的時候只不過是在已經生成好的sql語句中增加了一個where部分語句。所以並沒有辦法在執行filter的時候來動態設定db_table。

對於問題中說的get也是一樣,因為get本身就是在執行完filter之後從_result_cache列表中獲取的資料(_result_cache[0])。

方案一

根據the5fire上面的分析,要想在執行具體查詢時修改db_table已經是不可能了(當然,如果你打算去重寫Model中Meta部分的邏輯以及Queryset部分的邏輯,就當我沒說,我只能表示佩服)。

所以只能從定義層面下手了。也就是我需要定義多個Model,同樣的欄位,不同的db_table。大概是這樣。

  1. classUser(models.Model):
  2. username = models.CharField(max_length=
    255)
  3. classMeta:
  4. abstract=True
  5. classUser1(User):
  6. classMeta:
  7. db_table ='user_1'# 預設情況下不設定db_table屬性時,Django會使用``<app>_<model_name>``.lower()來作為表名
  8. classUser2(User):
  9. classMeta:
  10. db_table ='user_2'

這樣在User.objects.get(id=3)的時候,如果按照模2計算,那就是User01.objects.get(id=3),笨點的方法就是寫一個dict:

  1. user_sharding_map ={
  2. 1:User1,
  3. 2:User2
  4. }
  5. def get_sharding_model(id):
  6. key = id %2+1
  7. return user_sharding_map[key]
  8. ShardingModel= get_sharding_model(3)
  9. ShardingModel.objects.get(id=3)

如果真的這麼寫那Python作為動態語言,還有啥用,你分128張表試試。我們應該動態創建出User01,User02,....UserN這樣的表。

  1. classUser(models.Model):
  2. @classmethod
  3. def get_sharding_model(cls, id=None):
  4. piece = id %2+1
  5. classMeta:
  6. db_table ='user_%s'% piece
  7. attrs ={
  8. '__module__': cls.__module__,
  9. 'Meta':Meta,
  10. }
  11. return type(str('User%s'% piece),(cls,), attrs)
  12. username = models.CharField(max_length=255, verbose_name="the5fire blog username")
  13. classMeta:
  14. abstract=True
  15. ShardingUser=User.get_sharding_model(id=3)
  16. user =ShardingUser.objects.get(id=3)

嗯,這樣看起來似乎好了一下,但是還有問題,id=3需要傳兩次,如果兩次不一致,那就麻煩了。Model層要為上層提供統一的入口才行。

  1. classMyUser(models.Model):
  2. # 增加方法 BY the5fire
  3. @classmethod
  4. def sharding_get(cls, id=None,**kwargs):
  5. assert id,'id is required!'
  6. Model= cls.get_sharding_model(id=id)
  7. returnModel.objects.get(id=id,**kwargs)

對上層來書,只需要執行MyUser.sharding_get(id=10)即可。不過這改變了之前的呼叫習慣 objects.get 。

不管怎麼說吧,這也是個方案,更完美的方法就不繼續探究了,在Django的ORM中鑽來鑽去尋找可以hook的點實在憋屈。

我們來看方案二吧

方案二

ORM的過程是這樣的,Model——> SQL ——> Model,在方案一中我們一直在處理Model——> SQL的部分。其實我們可以拋開這一步,直接使用raw sql。

QuerySet提供了raw這樣的介面,用來讓你忽略第一層轉換,但是有可以使用從SQL到Model的轉換。只針對SELECT的案例:

  1. classMyUser(models.Model):
  2. id = models.IntegerField(primary_key=True, verbose_name='ID')
  3. username = models.CharField(max_length=255)
  4. @classmethod
  5. def get_sharding_table(cls, id=None):
  6. piece = id %2+1
  7. return cls._meta.db_table + str(piece)
  8. @classmethod
  9. def sharding_get(cls, id=None,**kwargs):
  10. assert isinstance(id,int),'id must be integer!'
  11. table = cls.get_sharding_table(id)
  12. sql ="SELECT * FROM %s"% table
  13. kwargs['id']= id
  14. condition =' AND '.join([k +'=%s'for k in kwargs])
  15. params=[str(v)for v in kwargs.values()]
  16. where=" WHERE "+ condition
  17. try:
  18. return cls.objects.raw(sql +where,params=params)[0]# the5fire:這裡應該模仿Queryset中get的處理方式
  19. exceptIndexError:
  20. # the5fire:其實應該拋Django的那個DoesNotExist異常
  21. returnNone
  22. classMeta:
  23. db_table ='user_'

大概這麼個意思吧,程式碼可以再嚴謹些。

總結

單純看方案一的話,可能會覺得這麼大量資料的專案,就別用Django了。其實the5fire第一次嘗試找一個優雅的方式hack db_table時,也是一頭灰。但是,所有的專案都是由小到大的,隨著資料/業務的變大,技術人員應該也會更加了解Django,等到一定階段之後,可能發現,用其他更靈活的框架,跟直接定製Django成本差不多。


參考連結:

https://www.the5fire.com/django-sharding-model.html

相關推薦

mysql 資料庫優化 django方案

由來這個問題戳到了Django ORM的痛點,對於多資料庫/分庫的問題,Django提供了很好的支援,通過using和db router可以很好的完成多資料庫的操作。但是說到分表的問題,就有點不那麼友好了。但也不是那麼難處理,只是處理起來不太優雅。解析在Django中,資料庫

MySQL查詢優化避免全掃描

原文地址:https://dev.mysql.com/doc/refman/5.7/en/table-scan-avoidance.html 譯文: 8.2.1.20 避免全表掃描 當MySQL使用全表掃描來解析查詢時,EXPLAIN的輸出結果中將在type列顯示ALL。這種情況通常發生

mysql資料庫優化GROUP BY(聚合) 函式

   group by    功能:功能:分類彙總的時候使用,表示按欄位分組。該語句對查詢結果按group by後的值分組,所有具有相同值元組為一組。  例:sql語句     select id,name,sum(num) count from st grou

MySQL資料庫分片(分庫

分庫分表 將存放在一個數據庫中的資料,按照特定方式進行拆分,分散到多個數據庫中,已達到分散單臺裝置負載的效果 垂直分割(縱向切分) 水平分割(橫向切分) 將單個表,拆分成多個表,分散到不同的資料庫 將單資料庫的多個表進

達達的mysql資料庫優化

https://mp.weixin.qq.com/s?__biz=MzA5Nzc4OTA1Mw==&mid=2659597585&idx=1&sn=8479e3d3fc276c639ace540fceb7319e&scene=0#rdMySQ

MYSQL效能優化資料庫的分庫

資料庫中的資料量不一定是可控的,在未進行分庫分表的情況下,隨著時間和業務的發展,庫中的表會越來越多,表中的資料量也會越來越大,相應地,資料操作,增刪改查的開銷也會越來越大;另外,由於無法進行分散式

關係型資料庫大資料效能優化解決方案(當前歷史)、分割槽、資料清理原則

原因和目的由於交易量大或者日積月累造成資料庫的資料量越來越大。會導致系統性能大幅下降,所以要對部分業務的表資料作備份和清理減少資料量,來提升請求響應的速度,提升使用者體驗資料是否需要清理的閥值判斷通常當表的磁碟大小超過 5GB,或對於 OLTP 系統(聯機事務處理),表的記錄

oracle遷移到mysql分庫方案——ogg(goldengate)

apply columns version alt ML -c testing name sam 之前文章主要介紹了oracle 遷移到mysql,主要是原表原結構遷移,但是實際運維中會發現,到mysql以後需要分庫和分表的拆分操作,這個時候,用ogg來做,也是很強大好用的

Discuz!教程大型Discuz!論壇站點帖子forum_post方案優化

forum_post表是儲存主題和回覆內容的表,是discuz系統中儲存內容最多的一個表。對於內容較多的大型站點來說,隨著這個表的逐漸增大,已經嚴重影響了站點的開啟速度。Discuz!系統本身已經有了帖子分表功能,但是每次都要手動操作分表,過一段時間之後主表(forum_po

mysql大數據分庫和 php解決方案

表結構 處理方式 很好 fig struct 提升性能 this 區別 turn 當Mysql數據量過大時,就會面臨壓力分解,這時分庫分表是一個不錯的解決方案,現在我們就來談談Mysql如何分庫分表比較理想,然後再用php如何調用。 1,主從復制,讀寫分離對主

MySQL分庫方案

人員 有趣的 而不是 其他 代理 延時 分片 -o 得到 1. MySQL分庫分表方案 1.1. 問題: 1.2. 回答: 1.2.1. 最好的切分MySQL的方式就是:除非萬不得已,否則不要去幹它。 1.2.2. 你的SQL語句不再是聲明式的(declarativ

MySQL主從(MySQL proxy Lua讀寫分離設置,一主多從同步配置,分庫方案

否則 count user username 2個 ons 基礎 zxvf 路徑 Mysql Proxy Lua讀寫分離設置一.讀寫分離說明讀寫分離(Read/Write Splitting),基本的原理是讓主數據庫處理事務性增、改、刪操作(INSERT、UPDATE、DE

【分庫、MySQL分庫方案

分表 性能 正常 事先 AD 現在 新用戶 我們 java 一、Mysql分庫分表方案 1.為什麽要分表: 當一張表的數據達到幾千萬時,你查詢一次所花的時間會變多,如果有聯合查詢的話,我想有可能會死在那兒了。分表的目的就在於此,減小數據庫的負擔,縮短查詢時間。 mys

MySQL 分庫方案,總結的非常好!

導致 一個 磁盤空間 所有 bsp 功能 編程 從庫 框架 前言 公司最近在搞服務分離,數據切分方面的東西,因為單張包裹表的數據量實在是太大,並且還在以每天60W的量增長。 之前了解過數據庫的分庫分表,讀過幾篇博文,但就只知道個模糊概念, 而且現在回想起來什麽都是模模糊糊的

MySQL效能管理及架構設計(三):SQL查詢優化、分庫 - 完結篇

一、SQL查詢優化(重要) 1.1 獲取有效能問題SQL的三種方式 通過使用者反饋獲取存在效能問題的SQL; 通過慢查日誌獲取存在效能問題的SQL; 實時獲取存在效能問題的SQL; 1.1.2 慢查日誌分析工具 相關配置引數: slow

MySQL 高可用:mysql+mycat實現資料庫分片(分庫

什麼是MYCAT: 一個徹底開源的,面向企業應用開發的大資料庫叢集 支援事務、ACID、可以替代MySQL的加強版資料庫 一個可以視為MySQL叢集的企業級資料庫,用來替代昂貴的Oracle叢集 一個融合記憶體快取技術、NoSQL技術、HDFS大資料的新型SQL Se

Oracle/MySQL 資料庫優化方案/方向

上週老闆說要做一個優化資料庫方案,上週沒做,週一的週會被批了一頓,前兩天出了一個草版方案,這裡記錄一下當沒有經驗沒有方向沒人指導的一個DBA如何著手做個數據庫優化方案和具體做法。   大方向: 優化無非就: sql優化 架構優化 硬體效能優化  

MySQL 分庫方案總結

1.為什麼要分表: 當一張表的資料達到幾千萬時,你查詢一次所花的時間會變多,如果有聯合查詢的話,我想有可能會死在那兒了。分表的目的就在於此,減小資料庫的負擔,縮短查詢時間。 mysql中有一種機制是表鎖定和行鎖定,是為了保證資料的完整性。表鎖定表示你們都不能對這張表進行操作,必須等我對

Linux資料庫管理——day10——分庫資料庫硬體優化

分庫分表     分庫分表也稱作分片技術,主要作用是將存放在一個數據庫中的資料按照特定的方法進行拆分,分散存放在多個數據庫中,以達到分散多臺裝置實現負載均衡     垂直分割        縱向切分,把一個表的表結構拆分開來,形成多個表        實質上就是把一個表的表

資料庫優化高效率調優oracle億級別

2017年在省公司做一個專案,涉及到一個億級別的大表操作,過程中遇到了很多坑,走過後記錄如下,方便今後回憶。 Oracle資料庫是一種事務性資料庫,對刪除、修改、新增操作會產生undo和redo兩種日誌,當一次提交的資料量過大時,資料庫會產生大量的日誌寫檔案IO操作,導致資