Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

在原有基础上实现了一个基于多种关联因素的(根据文件大小差异,权重可配置 ,访问比例,命中率等)size_adjusted 动态自调整的多级LRU cache,一些环境下对比测试,性能有很大提高 #486

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

pengshanzhou
Copy link

1.首先说一个场景:对于300000个1KB的小文件和300个1M 的大文件,虽然总大小一样,但是nginx和后端就此传输代价不一样(测试时差在十三倍左右,两个笔记本做测试所得) 基于此,我设计出基于多种关联因素的 size_adjusted (根据文件大小差异权重可配置)动态自调整的多级LRU cache。
2.通过动态的改变 每个层级的LRU 的max_size 达到更多的缓存 更有价值的文件,提高cache 性能,调整因素包括:各层在这一阶段的访问大小,各层在这时段的命中字节/自身max_size,各层权重,调整前的各层的max_size等等相关因素。
3.随机对比测试(测试环境为两个笔记本,磁盘一般) 改进后性能提高一倍, 测试情景简单表述:1000个1Kb的小文件和1M 大文件交替访问,后端一共有30000个小文件和30个大文件,一共测试100个总轮回,随机测试,(随机测试对磁盘不能跨度很大,否则笔记本的磁盘的寻道排队会成为瓶颈,生产环境的SSD或者其他磁盘会好很多,所以我的测试总的文件大小占据的磁盘空间不能很大(200M)否则大范围的随机寻道会带来大瓶颈)
4.很好的上限控制(根据size动态监测),防止max_size过大造成浪费


typedef struct {
level levels[10];
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最大是9级,另外还有一个0级: 暂时存放没有开始传输的文件

@cfsego
Copy link
Member

cfsego commented Jul 18, 2014

能否把性能测试步骤描述一下,最好添加一个性能测试脚本。

@pengshanzhou
Copy link
Author

脚本主要分两部分:一个是为后端apache2产生文件的脚本,一个是测试脚本。

python3.3.5 for win7(同学的笔记本,磁盘性能一般) 这是后端apache产生文件的脚本 在自建的 /www下
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

import os

class Tool:
    def file_generator(start,end):
        print(start)
        for i in range(start,end-30+1):   
            path = str(i) + '.txt' 
            f = open(path,'w')
            f.write('1') # 这里的文件传到nginx会加http包头 大小变成1KB(不是一个字节)
            f.close()

        for i in range(end-30+1,end+1):
            path =  str(i) + '.txt'
            f = open(path,'w')
            f.write('1'*1024*(1000-6)) 
            f.close()

    def file_remove(start,end):
        for i in range(start,end):
            path =  str(i) + '.txt'
            os.remove(path)


tool = Tool
tool.file_generator(1,30030)  #这里为了减低磁盘大范围的寻道排队带来的影响 所以总的大小不能设太大总共60M, 30M 的小文件 30M 的大文件。 生产环境下的磁盘寻道时间可能要小很多

后端同学的电脑的系统是win7系统 不是很了解NTFS寻道策略,但是大范围的随机访问(最开始我用1.2G=600M小文件+600M大文件) 即使命中率很高,只要随机范围很大,性能没有变化(纵向比较,自身无变化) 这个结论是和顺序访问做了对比后得出的(顺序访问要快很多,而且nginx命中率和速度正相关,而大范围的随机,很慢,命中率即使很高也很没有变化, 缩小范围减少寻道代价就OK)

@pengshanzhou
Copy link
Author

这里是测试脚本 随机 批量交替的连续访问,计时,最后对比。。。。(可回归测试)

#!/usr/bin/python2.7   for linux 


import time
from   urllib import urlopen
import random

class Tool:
    def __init__(self):
        print "tool start...."



    def random_file_generator(self,end):     #把随机序列写入文件   这是为了对比测试的时候 两个版本的nginx用同一个随机序列
        ran = ''
        i=0

        while i<end:
            j=0
            while j<1000:
                r=random.randint(1,30001)      #每1000个 1KB随机小文件序列 
                ran =ran +str(r)+  ' '
                j=j+1
            r=random.randint(30001,30031)              #交替就有一个1M的大文件序列
                                                                           # 交替生成 ,大小文件  总大小比例一样 
            ran = ran + str(r) + ' '
            i=i+1
        path='/usr/pengshan/random'
        f=open(path,'w')
        f.write(ran)
        f.close()

        #print result

    def random_file_read(self):    # 读取随机文件 返回序列(字符串)
        path='/usr/pengshan/random'
        f=open(path,'rw')
        re = f.read()
        #print re
        f.close()
        result = re.split(' ')
        #print len(result)
        return result


    def url_open_random(self,end):
        out=''

        re = Tool.random_file_read(self)
        path2='/usr/test/result2.txt'    # 测试结果写入各自文件  改进后的nginx 我写入result1.txt   改进前的nginx我写入 result2.txt
        #print re
        time0=time.time()
        time1=time0
        time3=time1
        f2=open(path2,'w')
        wol = " test result ...." + '\n'
        f2.write(wol)

        for i in range(0,end*1001):
            path = 'http://localhost:8888/www/' + re[i] + '.txt'    #ngixn listen这个url 反向到后端的apache 
            urlopen(path)
            if i%1001 == 0:                                   #每访问2M 输出用时
                time2 = time.time()
                gap = time2 -time1
                out=out+ str(i/1010) +'   '+ str(gap) + '\n'
                #time1 = time2
                print out

                if i%10010 == 0:
                    out = out+str( (time2-time3)/10) +'\n'   #以上每十次输出十次的平均时间
                    print out

                f2.write(out)
                f2.flush()
                out = ''
                #time.sleep(4)
                time1 = time.time()
                if i%10010==0:
                    time3 = time.time()

        f2.close()
tool = Tool()
tool.random_file_generator(500)  # 随机序列写入文件,第二次对比测试的时候不用再产生了 直接读取就行了 这样 对比的时候 两者都是用的一样的随机序列  500*1001个序列
tool.url_open_random(500) # 500*1001次

@pengshanzhou
Copy link
Author

测试结果
改进前:result2.txt
test result ....
0 0.0303821563721
0.00303821563721
0 5.78225803375
1 6.00370407104
2 6.0701611042
3 6.03430891037
4 5.59878396988
5 7.09409403801
6 6.37565493584
7 6.25361800194
8 6.03964495659
9 6.03681492805
6.12898490429
10 5.54879307747
11 8.02513289452
12 5.65263485909
13 5.51205301285
14 5.61681008339
15 5.57208585739
16 7.25607895851
17 5.44408202171
18 4.99330401421
19 4.9721801281
5.85946059227
20 5.17496395111
21 4.83814501762
22 4.76742196083
23 6.80850982666
24 5.26904797554
25 5.03308105469
26 5.35989093781
27 5.06068301201
28 5.36770796776
29 4.95718121529
5.2637458086
30 6.35174202919
31 4.79621696472
32 4.87576985359
33 4.36741089821
34 3.65173101425
35 3.90021300316
36 4.53995108604
37 5.27394509315
38 4.44707298279
39 4.18101406097
4.63857870102
40 4.5791618824
41 4.49689006805
42 4.34604001045
43 4.56612491608
44 4.53344202042
45 5.70942902565
46 4.31262111664
47 5.0141518116
48 4.18142819405
49 4.60542297363
4.6345649004
50 4.72979402542
51 4.71673178673
52 4.67688798904
53 4.67696809769
54 4.17127490044
55 4.2279920578
56 4.1477959156
57 4.42521691322
58 4.57899188995
59 3.54031181335
4.38926680088
60 3.17024683952
61 4.37245583534
62 3.91495800018
63 4.45921301842
64 4.33421492577
65 4.09996509552
66 4.28214812279
67 4.82723999023
68 3.74519491196
69 4.41311192513
4.16605529785
70 4.17727804184
71 3.72133207321
72 5.34028100967
73 4.41387009621
74 3.94591093063
75 4.29625916481
76 4.1530008316
77 4.26600313187
78 6.3788459301
79 4.59326505661
4.52871079445
80 4.46901106834
81 4.63364005089
82 4.6307990551
83 4.66947507858
84 5.17513298988
85 4.37989091873
86 5.20971894264
87 4.56403017044
88 4.89882111549
89 4.57043504715
4.72019300461
90 5.33493995667
91 4.90176796913
92 4.67403697968
93 5.12514710426
94 4.85368895531
95 4.69648003578
96 4.61008906364
97 4.5439748764
98 4.7052090168
99 4.4003469944
4.78468148708
100 4.57888197899
101 4.74814414978
102 4.55831408501
103 4.50779294968

@pengshanzhou
Copy link
Author

改进后nginx测试结果 result1.txt 大概在序号20的时候 开始执行分层的max_size的调整 这个改进还是很明显的
test result ....
0 0.0278210639954
0.00278210639954
0 5.56452298164
1 6.35396790504
2 5.91746687889
3 5.92192006111
4 5.82291507721
5 5.46581101418
6 5.6087641716
7 6.49218416214
8 5.51406502724
9 5.29771900177
5.79602360725
10 4.89202094078
11 5.10343194008
12 5.05907011032
13 4.89018392563
14 4.46526503563
15 4.89908289909
16 4.63382005692
17 4.62951397896
18 4.57729387283
19 4.42258191109
4.75731730461
20 3.92391991615
21 4.10835313797
22 6.67098712921
23 4.20624613762
24 4.07964110374
25 4.06543898582
26 4.30793213844
27 4.09205698967
28 4.18299484253
29 3.92426085472
4.35628759861
30 6.1788380146
31 3.84900712967
32 4.0927131176
33 3.7959280014
34 3.93838000298
35 3.6133620739
36 3.70633006096
37 3.49337100983
38 3.48220205307
39 3.1718211174
3.93227910995
40 3.58380794525
41 3.45845508575
42 3.20936799049
43 3.54947590828
44 3.1380469799
45 3.11107993126
46 3.47368407249
47 3.25594711304
48 3.1775329113
49 2.86011886597
3.28187658787
50 2.67919492722
51 2.97560095787
52 3.0468018055
53 2.79061985016
54 2.8311548233
55 2.79623413086
56 2.48667311668
57 2.84018802643
58 2.56329202652
59 2.59883403778
2.76091961861
60 2.71909093857
61 2.78521585464
62 1.64118289948
63 1.74580597878
64 2.54204010963
65 2.25031709671
66 2.63716697693
67 2.4914598465
68 2.79956197739
69 2.26853299141
2.38809831142
70 2.36435818672
71 2.24573802948
72 2.31689786911
73 2.16265583038
74 2.29000496864
75 2.21773290634
76 2.0791079998
77 1.97407579422
78 1.49183797836
79 1.83839702606
2.09814832211
80 2.03159093857
81 1.97300291061
82 2.04988098145
83 1.7789349556
84 1.78636288643
85 1.95821499825
86 1.94672203064
87 1.86612200737
88 2.05162811279
89 1.78929305077
1.92323870659
90 1.64431381226
91 1.72088909149
92 1.82864618301
93 1.87636709213
94 1.62507200241
95 1.80178999901
96 2.06888890266
97 1.51921796799
98 1.40169501305
99 1.77842402458
1.72657740116
100 1.72497701645
101 1.48896503448

@pengshanzhou
Copy link
Author

改进前后都是用同一个nginx.conf:

#user  administrator;
worker_processes  1;
daemon off;
#master_process off;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    use epoll;
    worker_connections 65535;
    #worker_connections  1024;
}


http {

    #设置磁盘缓存是30M ,而我的测试文件是60M,整体命中率在50%左右 
    proxy_temp_path /usr/local/nginx/temp;
    proxy_cache_path /usr/local/nginx/cache levels=1:2 keys_zone=one:40m inactive=1d max_size=30m;

    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       8888;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            #proxy_store on;
            proxy_pass  http://192.168.0.2;

            expires      1d;
            proxy_set_header Host  $host;
            proxy_set_header X-Forwarded-For  $remote_addr;
            proxy_cache one;
            proxy_cache_valid any      10h;
            root   html;
            index  index.html index.htm;
        }
    }
}

@pengshanzhou
Copy link
Author

目前测试的结论是:文件大小差异带来的网络传输的速率差异也是很大的(这个我做了很多对比测试), 如果nginx的磁盘足够大(大到足以对命中率没有影响),那么我实现的所谓的分层的意义不是很大,如果磁盘相对宝贵,改进后的算法可能会有一定的意义(至于多大?没有在生产环境下跑一跑,另外还需要合理的配置(程序里面的weight等等,还有一些常系数的调整)需要多去尝试)

@pengshanzhou
Copy link
Author

在这个测试用例中: 按照原来的LRU算法,那么大文件和小文件的命中率都在50%左右, 改进后的程序会按照改进算法和配置 提高小文件的缓存空间的大小(当然就缩小了大文件的空间,不过代价是值得的) 这样一来,就提高了小文件的缓存命中率,减少了apache向nginx传输小文件的概率,最难得性能得到很大提高。
这个测试用例还很单纯, 实际上改进的程序对分层不同的命中率,访问比例等因素,的变化都有很好的相应调整,性能也相比较好,这些我都做过测试 当然常系数还需要调整
我实现的ngx_http_file_cache_size_adjusted(ngx_http_file_cache_t * cache)调整函数非常复杂,实际上我非常想简单化,但是很无奈,在做了大量测试后,期间发现很多坑,最基本几条原则(目前已经可以满足):
1.合理的系数调整之后,能自适应环境的变化(访问比例,命中率等)。 相对于原有的单纯的LRU,性能至少不能出现下降。 调整因素要具有实际意义。
2 幅度要可控
3 不能存在浪费(或者过度浪费) 动态监测上限
4 各层max_size之和要等于总的max_size
还有很多细节问题。。。。。

@pengshanzhou pengshanzhou changed the title 在原有基础上实现了一个基于多种关联因素的 size_adjusted (根据文件大小差异权重可配置)动态自调整的多级LRU cache,一些环境下对比测试,性能有很大提高 在原有基础上实现了一个基于多种关联因素的(根据文件大小差异,权重可配置 ,访问比例,命中率等)size_adjusted 动态自调整的多级LRU cache,一些环境下对比测试,性能有很大提高 Jul 19, 2014
@yunoasgit
Copy link

yunoasgit commented Apr 3, 2019

ack #1214
how to set size value of check_shm_size?

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants