99热99这里只有精品6国产,亚洲中文字幕在线天天更新,在线观看亚洲精品国产福利片 ,久久久久综合网

歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋(píng)果6袋
6
麥子學(xué)院

Django分表的兩個(gè)方案

發(fā)布時(shí)間:2017-05-16 21:02  回復(fù):0  查看:2467   最后回復(fù):2017-05-16 21:02  
本文和大家分享的主要是django 分表相關(guān)內(nèi)容,一起來(lái)看看吧,希望對(duì)大家 學(xué)習(xí)django有所幫助。
   由來(lái)
   Django  分表怎么實(shí)現(xiàn)?
  這個(gè)問(wèn)題戳到了Django ORM 的痛點(diǎn),對(duì)于多數(shù)據(jù)庫(kù) / 分庫(kù)的問(wèn)題, Django 提供了很好的支持,通過(guò) using db router 可以很好的完成多數(shù)據(jù)庫(kù)的操作。但是說(shuō)到分表的問(wèn)題,就有點(diǎn)不那么友好了。但也不是那么難處理,只是處理起來(lái)不太優(yōu)雅。
   解析
  在Django 中,數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)的邏輯基本上是在 Queryset 中完成的,一個(gè)查詢(xún)請(qǐng)求,比如:User.objects.filter(group_id=10)  。
  其中的 objects  其實(shí)就是 models.Manager  ,而 Manager  又是對(duì)QuerySet 的一個(gè)包裝。而 QuerySet 又是最終要轉(zhuǎn)換為 sql 的一個(gè)中間層(就是 ORM 種,把 Model 操作轉(zhuǎn)換為 SQL 語(yǔ)句的部分)。所以當(dāng)我們寫(xiě)下 User.objects 的時(shí)候,就已經(jīng)確定了要訪(fǎng)問(wèn)的是哪個(gè)表了,這是由class Meta 中的 db_table 決定的。
  class User(models.Model):
  username = models.CharField(max_length=255)
  class Meta:
  db_table = 'user'
  理論上講,我們可以通過(guò)在運(yùn)行時(shí)修改db_table 來(lái)完成分表 CRUD 的邏輯,但是 the5fire 在看了又看源碼之后,還是沒(méi)找到如何下手。還是上面的問(wèn)題,當(dāng)執(zhí)行到 User.objects  的時(shí)候,表已經(jīng)確定了,當(dāng)執(zhí)行到User.objects.filter(group=10)  的時(shí)候只不過(guò)是在已經(jīng)生成好的sql 語(yǔ)句中增加了一個(gè) where 部分語(yǔ)句。所以并沒(méi)有辦法在執(zhí)行 filter 的時(shí)候來(lái)動(dòng)態(tài)設(shè)置 db_table 。
  對(duì)于問(wèn)題中說(shuō)的get 也是一樣,因?yàn)?/span> get 本身就是在執(zhí)行完 filter 之后從 _result_cache 列表中獲取的數(shù)據(jù)( _result_cache[0] )。
   方案一
  根據(jù)the5fire 上面的分析,要想在執(zhí)行具體查詢(xún)時(shí)修改 db_table 已經(jīng)是不可能了(當(dāng)然,如果你打算去重寫(xiě) Model Meta 部分的邏輯以及 Queryset 部分的邏輯,就當(dāng)我沒(méi)說(shuō),我只能表示佩服)。
  所以只能從定義層面下手了。也就是我需要定義多個(gè)Model ,同樣的字段,不同的 db_table 。大概是這樣。
  class User(models.Model):
  username = models.CharField(max_length=255)
  class Meta:
  abstract = True
  class User1(User):
  class Meta:
  db_table = 'user_1'  #  默認(rèn)情況下不設(shè)置 db_table 屬性時(shí), Django 會(huì)使用 ``_``.lower() 來(lái)作為表名
  class User2(User):
  class Meta:
  db_table = 'user_2'
  這樣在 User.objects.get(id=3)  的時(shí)候,如果按照模2 計(jì)算,那就是 User01.objects.get(id=3)  ,笨點(diǎn)的方法就是寫(xiě)一個(gè)dict:
  user_sharding_map = {
  1: User1,
  2: User2
  }
  def get_sharding_model(id):
  key = id % 2 + 1
  return user_sharding_map[key]
  ShardingModel = get_sharding_model(3)
  ShardingModel.objects.get(id=3)
  如果真的這么寫(xiě)那Python 作為動(dòng)態(tài)語(yǔ)言,還有啥用,你分 128 張表試試。我們應(yīng)該動(dòng)態(tài)創(chuàng)建出 User01,User02,....UserN 這樣的表。
  class User(models.Model):
  @classmethod
  def get_sharding_model(cls, id=None):
  piece = id % 2 + 1
  class Meta:
  db_table = 'user_%s' % piece
  attrs = {
  '__module__': cls.__module__,
  'Meta': Meta,
  }
  return type(str('User%s' % piece), (cls, ), attrs)
  username = models.CharField(max_length=255, verbose_name="the5fire blog username")
  class Meta:
  abstract = True
  ShardingUser = User.get_sharding_model(id=3)
  user = ShardingUser.objects.get(id=3)
  嗯,這樣看起來(lái)似乎好了一下,但是還有問(wèn)題,id=3 需要傳兩次,如果兩次不一致,那就麻煩了。 Model 層要為上層提供統(tǒng)一的入口才行。
  class MyUser(models.Model):
  增加方法  BY the5fire    @classmethod
  def sharding_get(cls, id=None, **kwargs):
  assert id, 'id is required!'
  Model = cls.get_sharding_model(id=id)
  return Model.objects.get(id=id, **kwargs)
  對(duì)上層來(lái)書(shū),只需要執(zhí)行MyUser.sharding_get(id=10) 即可。不過(guò)這改變了之前的調(diào)用習(xí)慣 objects.get  。
  不管怎么說(shuō)吧,這也是個(gè)方案,更完美的方法就不繼續(xù)探究了,在Django ORM 中鉆來(lái)鉆去尋找可以 hook 的點(diǎn)實(shí)在憋屈。
  我們來(lái)看方案二吧
   方案二
  ORM 的過(guò)程是這樣的, Model——> SQL ——> Model ,在方案一中我們一直在處理 Model——> SQL 的部分。其實(shí)我們可以?huà)侀_(kāi)這一步,直接使用 raw sql 。
  QuerySet 提供了 raw 這樣的接口,用來(lái)讓你忽略第一層轉(zhuǎn)換,但是有可以使用從 SQL Model 的轉(zhuǎn)換。只針對(duì) SELECT 的案例 :
  class MyUser(models.Model):
  id = models.IntegerField(primary_key=True, verbose_name='ID')
  username = models.CharField(max_length=255)
  @classmethod
  def get_sharding_table(cls, id=None):
  piece = id % 2 + 1
  return cls._meta.db_table + str(piece)
  @classmethod
  def sharding_get(cls, id=None, **kwargs):
  assert isinstance(id, int), 'id must be integer!'
  table = cls.get_sharding_table(id)
  sql = "SELECT * FROM %s" % table
  kwargs['id'] = id
  condition = ' AND '.join([k + '=%s' for k in kwargs])
  params = [str(v) for v in kwargs.values()]
  where = " WHERE " + condition
  try:
  return cls.objects.raw(sql + where, params=params)[0]  # the5fire: 這里應(yīng)該模仿 Queryset get 的處理方式
  except IndexError:
  # the5fire: 其實(shí)應(yīng)該拋 Django 的那個(gè) DoesNotExist 異常
  return None
  class Meta:
  db_table = 'user_'
  大概這么個(gè)意思吧,代碼可以再?lài)?yán)謹(jǐn)些。
   總結(jié)
  單純看方案一的話(huà),可能會(huì)覺(jué)得這么大量數(shù)據(jù)的項(xiàng)目,就別用Django 了。其實(shí) the5fire 第一次嘗試找一個(gè)優(yōu)雅的方式 hack db_table 時(shí),也是一頭灰。但是,所有的項(xiàng)目都是由小到大的,隨著數(shù)據(jù) / 業(yè)務(wù)的變大,技術(shù)人員應(yīng)該也會(huì)更加了解 Django ,等到一定階段之后,可能發(fā)現(xiàn),用其他更靈活的框架,跟直接定制 Django 成本差不多。

來(lái)源:the5fire 的技術(shù)博客
您還未登錄,請(qǐng)先登錄

熱門(mén)帖子

最新帖子

?