现在的位置: 首页 > 综合 > 正文

统计IP——多线程统计数据

2014年01月27日 ⁄ 综合 ⁄ 共 7200字 ⁄ 字号 评论关闭

一个机房对应多个机架,一个机架对应多个服务器,一个机房对应多个机房网络,一个机房网络相当于一个IP段。在添加服务器的时候会为该服务器指定相应的内网IP和外网IP,查询IP的时候根据服务器的资产编号和IP类型(内网和外网)去查,要把所有符合条件的IP都查出来供服务器选择,下面就讲三种方法统计IP:

第一种:

根据资产编号——》服务器——》机架——》机房——》机房网络列表

然后遍历机房网络列表,把查出的IP合并到一起。

'''用字符串拼接,最原始的方法'''
    d1 =  datetime.datetime.now()
    
    nocnetSet = NOCNetInfo.objects.filter(nocinfo = nocinfo)
    ips = []
    if nocnetSet:
        nocnetinfo = list(nocnetSet)
        for nocnet in nocnetinfo:
            
            ipstart = nocnet.ip_start
            ipend = nocnet.ip_end
            
            ip_st = ipstart.split('.')
            ip_et = ipend.split('.')
            start = int(ip_st[3])
            end = int(ip_et[3])
            while True:
                ip_st[3] = str(start)
                ipAddress1 = ip_st[0] +'.'+ ip_st[1] +'.'+ ip_st[2] +'.'+ ip_st[3]
                ip_t = IPModel.objects.filter(ipAddress = ipAddress1, ipType = ipType, noc_id = nocinfo.id)#IPType。“0”为内网、“1”为外网
                if ip_t:
                    ips.append(list(ip_t).pop(0))
                start += 1
                if start == end+1:break;
    
    print "ips: " + str(len(ips))            
    print ips            
    d2 =  datetime.datetime.now()
    d =  d2 - d1
    print d

第二种:

跟第一种类似,资产编号——》服务器——》机架——》机房——》机房网络列表

然后遍历机房网络列表,把查出的IP合并到一起。

不同的是不同的机房网络查出来的IP合并到一起的方法。这里采用系统自带的list方法:extend

'''用list的extend方法实现'''
    d1 =  datetime.datetime.now()
    nocnetinfos = nocinfo.nocnetinfo_set.all()
    ips = []
    for nocnetinfo in nocnetinfos:
        ippools = IPModel.objects.filter(Q(ipAddress__gte=nocnetinfo.ip_start) & Q(ipAddress__lte=nocnetinfo.ip_end) & Q(ipType__exact=ipType))
        ips.extend(ippools)
    
    print "ips: " + str(len(ips))
    d2 =  datetime.datetime.now()
    d =  d2 - d1
    print d

第三种:

前面也跟第一种类似,

资产编号——》服务器——》机架——》机房——》机房网络列表

然后遍历机房网络列表,把查出的IP合并到一起。

主要还是合并IP的方法的区别,这里采用多线程实现,每个机房网络开一个线程。

'''用多线程实现'''
    d1 =  datetime.datetime.now()
    nocnetinfos = nocinfo.nocnetinfo_set.all()
    ips_2 = []
    
    lock = threading.Lock()  
    for nocnetinfo in nocnetinfos:
        thread_id = Counter(ips_2, nocnetinfo, ipType, lock, "thread-" + str(nocnetinfo.id))
        thread_id.start()
        thread_id.join()
    
    print "ips_2: " + str(len(ips_2))
    d2 =  datetime.datetime.now()
    d =  d2 - d1
    print d

线程类Counter:

import threading

'''
    线程类
'''

class Counter(threading.Thread):  
    def __init__(self, ips_2, nocnetinfo, ipType, lock, threadName):  
        '''''@summary: 初始化对象。 
        @param lock: 琐对象。 
        @param threadName: 线程名称。 
        '''  
        super(Counter, self).__init__(name=threadName)  #注意:一定要显式的调用父类的初始化函数。  
        self.ips_2 = ips_2  
        self.nocnetinfo = nocnetinfo  
        self.ipType = ipType  
        self.lock = lock  
        
    def run(self):  
        '''''@summary: 重写父类run方法,在线程启动后执行该方法内的代码。 
        '''  
        self.lock.acquire()
        ippools = IPModel.objects.filter(Q(ipAddress__gte=self.nocnetinfo.ip_start) & Q(ipAddress__lte=self.nocnetinfo.ip_end) & Q(ipType__exact=self.ipType))
        self.ips_2.extend(ippools)
        self.lock.release()  

这里面有两点需要注意:

1.给函数传参数ips_2,因为ips_2是list类型,所以在多线程类Counter中对ips_2的修改会直接影响到主线程中的ips_2,在这里也正是我所需要的

2.启动每一个线程后要调用该线程的join方法,起到阻塞主线程的作用,不然在子线程没有执行完的情况下就会去执行主线程,这样会得到错误的结果,并不是我所希望的。

简单做了测试:

在查询200条数据的情况下,第一种要5000毫秒左右,第二种在500毫秒左右,第三种利用多线程反而比第二种慢了一点,大约在700毫秒左右(应该是数据量太少,没有体现出多线程的优势)。

在查询5000条数据的情况下,第一种大约要10000毫秒左右,第二种要5000毫秒左右,第三种大约也是5000毫秒

总的来说还是很慢,满足不了需求,后来经过研究,django的查询集是惰性的,如果只是得到查询集,不去操作数据库,速度将会很快,最其码能够提高十倍。

但在python代码中执行extend操作时就会去操作数据库,所以速度给降下来了,要想提高速度,只有改变实现方式,现在的思路是:

针对不同的机房网络,写出对应的查询条件,然后把查询条件拼接到一起,然后再执行查询,这样只是得到了查询集,没有去操作数据库,所以速度很快。

实现代码:

'''拼接查询条件,利用查询集的缓存'''
    d1 =  datetime.datetime.now()
    nocnetinfos = nocinfo.nocnetinfo_set.all()
    search = ''
    for nocnetinfo in nocnetinfos:
        ip_start = IPModel.objects.get(ipAddress__exact=nocnetinfo.ip_start)
        ip_end = IPModel.objects.get(ipAddress__exact=nocnetinfo.ip_end)
        search += 'Q(id__gte=' + str(ip_start.id) + ') & Q(id__lte=' + str(ip_end.id) + ') & Q(ipType__exact=' + str(ipType) + ') | '
    
    search = search[0:-2]
#    search = 'Q(log_type__exact=log_type) & Q(relate_id__exact=relate_id) | Q(sequence__startswith=sequence)'
    print search
    ips_3 = IPModel.objects.filter(eval(search))
    
#    print "ips_3: " + str(len(ips_3))
    print "ips_3: "
    d2 =  datetime.datetime.now()
    d =  d2 - d1
    print d

里面有三点需要注意:

1.以前查询IP用>=192.168.1.1,<= 192.168.1.255这样的形式是不行的,这样的话192.168.1.3是不在这个范围的,实际上这个是我们想得到的数据。所以现在换成了用主键id

2.不能执行print len(ips_3)这行代码,因为执行这行代码时会去操作数据库,速度就降下来了。

不能调用len函数获得查询集中数据中个数,可以调用查询管理器的count方法,用法如下:

count()

返回数据库中匹配查询(QuerySet)的对象数量。 count() 不会抛出任何异常。

例如:

# Returns the total number of entries in the database.
Entry.objects.count()

# Returns the number of entries whose headline contains 'Lennon'
Entry.objects.filter(headline__contains='Lennon').count()

count() 会在后端执行 SELECT COUNT(*) 操作,所以你应该尽量使用 count() 而不是对返回的查询结果使用 len() 。

根据你所使用的数据库(例如 PostgreSQL 和 MySQL),count() 可能会返回长整型,而不是普通的 Python 整数。这确实是一个很古怪的举措,没有什么实际意义。

3.查询条件拼接好后实际上是一个字符串,直接传给

IPModel.objects.filter(search)

这样是不行的,要对search用eval函数解析一下,如下所示:

IPModel.objects.filter(eval(search))

这种情况测试了一下:在5000条数据的情况下500毫秒就全查出来了。哈哈,这才是我需要的。

关于查询集的惰性用法:

查询集是惰性的

查询集 是惰性的,即创建一个 查询集 不会触动数据库。你可以不停地创建 过滤器,但 Django 不会真正运行查询,除非 查询集 被 执行 了。来看以下的
例子:

>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.now())
>>> q = q.exclude(body_text__icontains="food")
>>> print q

上例中表面看执行了三个数据库查询,但实质只有最后一行代码( print q )执行了 一次数据库查询。通常,只到你“要求”返回结果时, 查询集 才会从数据库中抓取
数据, 查询集 被 执行 了。更多关于执行的精确时间的内容参见 When
QuerySets are evaluated
 。

关于查询集什么时候真正去操作数据库:

从内部讲,QuerySet 可以被构造,过滤,切片,做为参数传递,这些行为都不会对数据库进行操作。只要你查询的时候才真正的操作数据库。

下面的 QuerySet 行为会导致执行查询的操作:

  • 循环(Iteration)QuerySet 是可迭代的,在你遍历对象时就会执行数据库操作。例如,打印出所有博文的大标题:

    for e in Entry.objects.all():
        print e.headline
    
  • 切片(Slicing):在 查询限定(Limiting QuerySets) 中提到,
    QuerySet 是可以用 Python 的数组切片语法完成切片。一般来说对一个 QuerySet 切片就返回另一个 QuerySet (新
    QuerySet 不会被执行)。不过如果你在切片时使用了 "step" 参数,Django 仍会执行数据库操作。

  • 序列化/缓存化(Pickling/Caching): 详情请查看 pickling
    QuerySets
    。 这一节所强调的一点是查询结果是从数据库中读取的。

  • repr(). 调用 QuerySet 的 repr() 方法时,查询就会被运行。这对于
    Python 命令行来说非常方便,你可以使用 API 立即看到查询结果。

  • len(). 调用 QuerySet 的 len() 方法,查询就会被运行。这正如你所料,会返回查询结果列表的长度。

    注意:如果你想得到集合中记录的数量,就不要使用 QuerySet 的 len() 方法。因为直接在数据库层面使用
    SQL 的 SELECT COUNT(*) 会更加高效,Django 提供了 count() 方法就是这个原因。详情参阅下面的 count() 方法。

  • list(). 对 QuerySet 应用 list() 方法,就会运行查询。例如:

    entry_list = list(Entry.objects.all())
    

    要注意地是:使用这个方法会占用大量内存,因为 Django 将列表内容都载入到内存中。做为对比,遍历 QuerySet 是从数据库读取数据,仅在使用某个对象时才将其载入到内容中。

关于数据何时被放到缓存中的:

缓存和查询集

每个 查询集 都包含一个缓存,用于最小化数据库使用。为了写出最有效率的代码, 理解缓存的工作方式是非常重要的。

当一个 查询集 新建时,其缓存是空的。当 查询集 第一次被执行时,数据库就 会使用一次, Django 会把查询结果保存在 查询集 的缓存中并返回结果。以后的
查询就会重复使用缓存。

你要小心缓存的行为,如果不正确使用 查询集 ,那么有可能上当。例如下列语句会 创建两个 查询集 并执行它们,然后把它们抛在一边:

>>> print [e.headline for e in Entry.objects.all()]
>>> print [e.pub_date for e in Entry.objects.all()]

这意味着两个同样的查询被执行了两次,浪费啊。同时,两条语句的条目还可能不一致, 因为在两次查询之间可能会有条目增减。

要避免以上问题,只要保存 查询集 并且重复使用就可以了:

>>> queryset = Entry.objects.all()
>>> print [p.headline for p in queryset] # 执行查询集。
>>> print [p.pub_date for p in queryset] # 重复使用缓存。

为了只得到查询集,不去真正操作数据库,经常会用到Q对象进行复杂的查询,Q对象用法如下:

使用 Q 对象进行复杂查询

在 filter() 等语句中的关键字查询参数都是“逻辑与”的关系。如果要进行更复杂的 查询(如条件之间为“逻辑或”的关系),那么可以使用 Q 对象。

Q 对象( django.db.models.Q )是用于封装一堆关键字查询参数的对象。这些 关键字查询参数在上文“字段查找”中已描述过。

例如,下面的 Q 对象封装了一个单一的 LIKE 查询:

from django.db.models import Q
Q(question__startswith='What')

Q 对象可以用 & 和 | 操作符联结,联结之后会产生一个新的 Q 对象。

下例产生了一个新的 Q 对象,这个对象表示两个 "question__startswith" 查询的 OR 的联结:

Q(question__startswith='Who') | Q(question__startswith='What')

上例相当于下面的 SQL WHERE 子句:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

可以用 & 和 | 操作符及圆括号来组合任意复杂的 Q 对象,还可以使用 ~ 符号来表示“逻辑非”。例如:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

每个有关键字参数的查询函数(如 filter() 、``exclude()`` 和 get() )可以 接受一个或多个 Q 对象作为位置参数(不是命名参数)。如果你提供了多个 Q 对象,则参数之间是“逻辑与”的关系。例如:

Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

... 相当于以下 SQL:

SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

在查询函数中可以混合使用 Q 对象和关键字参数。查询函数中的所有参数(包括 Q 对象和关键字参数)都是“逻辑与”的关系。如果混合使用这两种参数, Q 对象应当放在关键字参数之前。例如:

Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who')

... 是合法的查询,相当于上一个例子,但:

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

... 是不合法的。

抱歉!评论已关闭.