覆蓋Django mysql model中save方法時碰到的一個數據庫更新延遲問題


最近有一個需求,通過django的admin后台,可以人工配置5張表的數據,這些數據進行一些業務規則處理后會統一成一份數據緩存在一個cache之中供服務端業務訪問,因而任何一張表的數據更新(增、刪、改),都要需要重新根據規則計算數據結果,並更新cache。

首先想到的方法就是覆蓋每個表model子類中的save方法,在其中先調用父類的save方法走原有保存邏輯更新數據到數據庫后,之后再單獨調用一次cache的更新邏輯,這樣每張表的任意數據被用戶更新后,都將先觸發model的數據庫更新、而后執行cache的數據更新,其中Application表model的代碼如下所示:

 1 class Application(models.Model):
 2     name = models.CharField(max_length=128, blank=False, verbose_name=u'應用名')
 3     description = models.TextField(blank=False, verbose_name=u'應用描述')
 4     status = models.IntegerField(verbose_name=u'狀態', choices=APPLICATION_STATUS)
 5     mtime = models.DateTimeField(blank=False, verbose_name=u'修改日期', auto_now=True)
 6     ctime = models.DateTimeField(blank=False, verbose_name=u'創建日期', auto_now_add=True)
 7     class Meta:
 8         db_table = 'application'
 9         verbose_name_plural = u'應用'
10 
11     def save(self, *args, **kargs):
12         super(Application, self).save(*args, **kargs)
13         # 更新memcached邏輯實現函數,該函數為通用函數一部分,會單獨建立mysql連接,查詢數據庫數據,並更新到memcached
14         update_memcached_from_mysql()

這樣每次在web上新增或者修改數據表記錄時,都會先執行父類save操作,save完成后,又會執行update_memcached_from_mysql函數,從mysql查詢到最新數據,而后更新到cache之中了。

然而實際測試的時候,發現每次修改數據時,更新到cache的並不是最新數據,而是未修改前的舊數據,比如當前name="test0",修改為name="test1"點擊保存后,更新到cache之中的確還是test0,再次修改為name="test2",更新到cache之中的確實test1。

百思不得其解~懷疑是model執行save時,本地有cache會延遲更新,於是在super.save和update_memcached_from_mysql之間增加了time.sleep(10),並多次調用update_memcached_from_mysql函數,可是依然是每次修改保存時,更新到cache的數據都是修改前的取值:

1     def save(self, *args, **kargs):
2         super(Application, self).save(*args, **kargs)
3         # 更新memcached邏輯實現函數,該函數為通用函數一部分,會單獨建立mysql連接,查詢數據庫數據,並更新到memcached
4         update_memcached_from_mysql()
5         time.sleep(10)
6         update_memcached_from_mysql()
7         time.sleep(10)
8         update_memcached_from_mysql()

想不出好的解決方案,猜測model真正將數據更新到數據的時機是在save整個函數執行結束后,臆測了如下更新邏輯:

1 子類save執行前
2 父類save執行
3 更新memcached
4 子類save執行結束
5 真正更新到數據庫

於是必須想辦法將第3步的cache更新邏輯挪到save執行結束后,然后要保證每次執行save操作時更新cache,這個位置又不能動~~

於是考慮通過開啟獨立線程異步執行的方式實現,改寫update_memcached_from_mysql,在其中開啟獨立線程執行一個delay版本的更新函數,線程start后會先休眠n秒鍾(n為可控參數,下例中為2),而后才執行從數據庫讀取數據並更新到cache的邏輯,改完后手動更新數據多次,驗證已經能拉取到最新數據。

1 def update_memcached_from_mysql():
2     """ 
3     猜測由於model的緩存機制,save函數執行完成前,新的數據可能未及時更新到數據庫,
4     此處開啟獨立線程執行memcache更新操作,線程中會休眠數秒再從數據庫拉取最新數據更新
5     """
6     td = threading.Thread(target=update_memcached_from_mysql_delay, args=(2, ))
7     td.start()

然而之前的更新流程還僅僅是猜測而已,雖然采用線程異步延遲更新cache的方法后,多次修改驗證避開了取不到新數據的問題,並不就說明猜測一定是正確的,而且即便猜測是正確的,如果save函數執行完后,model的數據更新沒有在線程延遲時間結束前完成,理論上還是會有問題,考慮可以通過設置一個定時任務,比如每隔10分鍾定時執行cache更新邏輯,來保證新數據最多延遲10分鍾也能生效。

本來想深入探究model save更新機制~然而最近太忙了~~blog都兩周沒更新了,初步嘗試了一下也還沒有研究清楚這一塊save邏輯的源碼,這個數據修改平台也僅供內部使用~~暫時先這么修補一下~~以后有時間再深究這一塊的問題~~加入TODO list。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM