技术开发 频道

高性能Web开发:减少数据库往返实例

        【IT168 技术】Web程序的后端主要有两个东西:渲染(生成HTML,或数据序列化)和IO(数据库操作,或内部服务调用)。今天要讲的是后面那个,关注一下如何减少数据库往返这个问题。最快的查询是不存在的,没有最快,只有更快!

  开始讲之前我得提一下Schema的重要性,但不会在这花太多时间。单独一个因素不会影响程序的整体响应速度,有调数据的能力,比有一个好的数据(库)Schema要强得多。这些东西以后会细讲,但Schema问题常会限制你的选择,所以现在提一下。

  我也会提一下缓存。在理想情况下,我要讨论的东西能有效减少返回不能缓存或缓存丢失的数据的时间,但跟通过优化查询减少数据库往返次数一样,避免将全部东西扔进缓存里是个极大的进步。

  最后得提一下的是,文中我用的是Python(Django),但原理在其他语言或ORM框架里也适用。我以前搞过Java(Hibernate),不太顺手,后来搞Perl(DBIX::Class)、Ruby(Rails)以及其他几种东西去了。

  N+1 Selects问题

  关于数据库往返最常见又让人吃惊的问题是n+1 selects问题。这个问题最简单的形式包括一个有子对象的实体,和一对多的关系。下面是一个小例子。

from django.db import models


class State(models.Model):
    name
= models.CharField(max_length=64)
    country
= models.ForeignKey(Country, related_name='states')

    class Meta:
        ordering
= ('name',)


class City(models.Model):
    name
= models.CharField(max_length=64)
    state
= models.ForeignKey(State, related_name='cities')

    class Meta:
        ordering
= ('name',)

  上面定义了州跟市,一个州有0或多个市,这个例子程序用来打印一个州跟市的内联列表。

Alaska
    Anchorage
    Fairbanks
    Willow
California
    Berkeley
    Monterey
    Palo Alto
    San Diego
    San Francisco
    Santa Cruz
Kentucky
    Albany
    Monticello
    Lexington
    Louisville
    Somerset
    Stamping Ground

  要完成这个功能的代码如下:

from django.shortcuts import render_to_response
from django.template.context import RequestContext
from locations.models import State

def list_locations(request):
    data
= {'states': State.objects.all()}
    
return render_to_response('list_locations.html', data,
                              RequestContext(request))

  如果将上面的代码跑起来,生成相应的HTML,通过django-debug-toolbar就会看到有一个用于列出全部的州查询,然后对应每个州有一个查询,用于列出这个州下面的市。如果只有3个州,这不是很多,但如果是50个,“+1”部分还是一个查询,为了得到全部对应的市,“N"则变成了50。

...
<ul>
{
% for state in states %}
<li>{{ state.name }}
    
<ul>
        {
% for city in state.cities.all %}
        
<li>{{ city.name }}</li>
        {
% endfor %}
    
</ul>
</li>
{
% endfor %}
</ul>
...
0
相关文章