Django实现大数据量分页查询

一开始是使用如下的代码:
    serverinfolist = ServerLog.objects.all()[0:1]
    paginator = Paginator(serverinfolist, PER_PAGE) # Show 25 contacts per page
    try:
        page = int(request.GET.get(’page’, ‘1′))
    except ValueError:
        page = 1
    try:
        contacts = paginator.page(page)        #取到具体的每一页的内容及属性
    except (EmptyPage, InvalidPage):
        contacts = paginator.page(paginator.num_pages)
       
    return render_to_response(’serverlinux/server_infos.html’, {”listobjects”: contacts,”flag”:True})

即利用了Django自带的分页类作分页处理。而且我在ORM一层中过滤了查询记录条数。

测试数据量:600万条!
结果时间:卡死!

怎么办?

GOOGLE~~~~

数据表600万条记录我写了一条查询语句
select * from tablename
结果 内存益出!!! 切记千万不要一次去提取这么多的数据出来。会搞死人的!
相当于是将全部的记录都提取到内存里面去不死才怪!

有个作者写的优化:
Django ORM using objects: 93 seconds Django ORM using values: 78 seconds Python: 51 seconds C extension for Python: 22 seconds Separate daemon in C communicating through UNIX sockets: 0.8 seconds C daemon highly optimized, multihreaded, using in-process caching and SSE3 assembler: 0.05 seconds 直接写C扩展效率是最快的。这种方法是可以考虑采用的!

p1 = ServerLog.objects.all()[0:1000]  我这样在命令SHELL中运行发现速度是比较可观的!
相当于是执行了limit 0,1000。
可是为什么我将其写在VIEW里面就速度慢下来了呢?  如果我用SQL语句的话就非常快了。原因是什么呢?
然道用ORM 的话就是效率提不起来吗?牺牲了效率换速度? 确实~~~~ ?谁能帮我分析一下~~

算了抛开ORM的东西直接使用原生SQL加Python吧!

我先这样测试下看看速度会不会提高!

SQL分页的原理:
select * from t limit 0,1;   第一页
select * from t limit 1,1;   第二页!

即  select * from t limit variables,1;
通过传过来的值进行相关的分页处理了!

解决方法一:
  也是初探不一定能够成功解决这个问题的。试一下吧
我还用Paginator 对象。不过呢我用了原生的SQL方法看能不能正确解决?

    serverinfolist = ServerLog.servertop()
    paginator = Paginator(serverinfolist, PER_PAGE) # Show 25 contacts per page

这样的话其实这个分页对象是从我们既定的数据集里面去提取的数据进行分页处理。
在下一页当数据全部提取完之后再去提一次数据或者可以这样每次提取的与分页的数据是一样多。

我发现这个对象中 第一页是 1 第二页是 2
这样的话 页面中显示为第一页的记录集其实SQL语句是:
select * from tablename limit 0,per_page;
应该是提取 select * from tablename limit 0,per_page+1;  表示要有下一页出来的!

比如说ID从 1–10
第一页提取的是:1 - 3   共3条
SQL语句:select * from tablename limit 0,3;           未尾ID值为 3
实际的SQL语句:select * from tablename limit 0,4;      未尾的ID值为 4

这样的话到页面就会有一个多余的记录出来有下一页传入新数字下一页数字即 2
第二页的SQL语句:
本来的为select * from tablename limit 3,3             即正常分页情况是提取3开头的!
由于前一页多了一条故而这里   select * from t limit 3,4;
前一个值的计算公司: (当前页数-1)*每页条数              只是后面那个偏移值要变化一下就OK了!

OK测试!!

try:
        page = int(request.GET.get(’page’, ‘1′))
    except ValueError:
        page = 1                            #默认为第一页
    logging.debug(page)
    serverinfolist = ServerLog.servertop(page,PER_PAGE)  #传了两个参数进来了
    paginator = Paginator(serverinfolist, PER_PAGE) # Show 25 contacts per page
    try:
        contacts = paginator.page(page)        #取到具体的每一页的内容及属性
    except (EmptyPage, InvalidPage):
        contacts = paginator.page(paginator.num_pages)
       
    return render_to_response(’serverlinux/server_infos.html’, {”listobjects”: contacts,”flag”:True})

测试:第二页虽然SQL语句是 提取下一页的数据的语句了。但是在页面显示层上却还是前一页的那个一条记录!
我想原因:paginator这个东西是将记录集提取出来之后保存到了内存了。下一页的记录是直接去提取它的记录集的。如果没有才会再去后端提取如果有就直接走这个对象的缓存集了。

所以这样做MS行不通。没事要不我将这个对象的缓存去掉试试?

看来得好好 去了解一下这个对象的一些东西了!

Paginator 的官方帮助文档

看了一下没有讲如何清掉办法!!!
算了看来得要自己写一个分页类了!!!!

写吧!
采用了最原始的方法来解决看看先!

我的写法:
try:
        page = int(request.GET.get(’page’, ‘1′))
    except ValueError:
        page = 1
    #–得到总共多少页,测试发现select count(*) from tablename 速度也是非常慢的!不考虑了
    serverinfolist = ServerLog.servertop(page,PER_PAGE)
    next_page = page + 1
    prior_page = 1 if(page == 1) else (page-1)
    return render_to_response(’serverlinux/server_info.html’, {”listobjects”: serverinfolist,”flag”:True,”privor_page”:prior_page,”next_page”:next_page})

这个时候模板应该要如何写来着?忘掉了哈哈!!
查下原来的笔记》》》

{% for type in listobjects %}
    <tr bgcolor=”#EBF2F9″ align=center>
    <td align=”center”>
    <label>{{type.0}}</label>
    </td>
   
    <td align=”center”>查阅</td>
    <td align=”center”>{{type.1}}</td>
    {%if flag %}
    <td align=”center”><a href=”/serverlinux/showxj/?foreignip={{type.2}}”>{{type.2}}</a></td>
    {%else%}
    <td align=”center”>{{type.2}}</td>
    {%endif%}
    <td align=”center”>{{type.3}}</td>
    <td align=”center”>{{type.4|serverinfofilter:0}}</td>
    <td align=”center”>{{type.5|serverinfofilter:1}}</td>
    <td align=”center”>{{type.6|serverinfofilter:2}}</td>
    <td align=”center”>{{type.7|serverinfofilter:3}}</td>
    <td align=”center”>{{type.8}}</td>
    <td align=”center”>{{type.9|serverinfofilter:5}}</td>
                            

一定要搞清楚一点:
对象的话是相当于JSON的东西。所以肯定是要.id 这样来访问其属性值了。
而如果是直接这样通过SQL语句查询出来的是生成了一个二元数组了。数组的下标肯定是数字了!
所以这里面要用数字了!

改进版:

try:
        page = int(request.GET.get(’page’, ‘1′))
    except ValueError:
        page = 1
    #–得到总共多少页,测试发现select count(*) from tablename 速度也是非常慢的!不考虑了
    serverinfolist = ServerLog.servertop(page,PER_PAGE)
    curr_count = len(serverinfolist)
    end_page = True if(curr_count == PER_PAGE) else False
    next_page = page + 1
    prior_page = 1 if(page == 1) else (page-1)
    first_page = False if(page == 1) else True          #标识是否需要第一页如果当前页为1则不需要了否则就需要了 
    return render_to_response(’serverlinux/server_info.html’, {”listobjects”: serverinfolist,”flag”:True,”privor_page”:prior_page,”next_page”:next_page,”first_page”:first_page,”page”:page,”end_page”:end_page})
   
最终版!
能够实现动态显示是否需要显示前一页、下一页的功能!而且直接在M层作了分页处理了非常方便!

至此算是一个终结。周未去问下有关于ORM实现分页的效率 问题看看
周日再续!!!

Share and Enjoy:
  • Print this article!
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • LinkedIn
  • Live
  • MySpace
  • RSS
  • Slashdot
  • Technorati
  • TwitThis

Related posts:

  1. python中的类型判定

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word

Contact us

Admin: Bryan Wu