免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
內(nèi)存優(yōu)化之Redis數(shù)據(jù)結(jié)構(gòu)的設(shè)計優(yōu)化實踐[原創(chuàng)分享] ? Hey! Linux.

參考資料:
http://www.mysqlops.com/2011/09/06/redis-kv-design.html
http://blog.nosqlfan.com/html/3379.html

通過對文章《節(jié)約內(nèi)存:Instagram的Redis實踐》的閱讀之后,感覺受益不少。
在文章中,Instagram 通過對數(shù)據(jù)結(jié)構(gòu)的設(shè)計優(yōu)化,使內(nèi)存從之前的21GB逐步降低到15GB,5GB最后到達了3GB,效果非常顯著。

因此自己打算在測試環(huán)境中模擬其思路,通過實踐加深理解并得出一些真實的數(shù)據(jù)。

首先,需要生成一些數(shù)據(jù),為了方便理解,我從本地CloudStack中的vm_instance表中取了一些數(shù)據(jù)。
下面我們來看一個關(guān)系型數(shù)據(jù)庫的設(shè)計:

1mysql> select id,instance_name,private_ip_address,uuid,created from vm_instance;
2+----+---------------+--------------------+--------------------------------------+---------------------+
3| id | instance_name | private_ip_address | uuid                                 | created             |
4+----+---------------+--------------------+--------------------------------------+---------------------+
5|  1 | s-1-VM        | 10.6.59.6          | 8c252255-82b8-4934-830e-0573cc9e0a1c | 2012-05-27 04:06:54 |
6|  2 | v-2-VM        | 10.6.88.209        | 1aae6ab9-73cb-46e3-aafb-985f6a143a08 | 2012-05-27 04:06:54 |
7|  4 | r-4-VM        | 169.254.1.42       | 5520f0e9-4c5a-4599-be5c-0ea74b59d6dd | 2012-05-27 10:45:42 |
8|  5 | i-2-5-VM      | 10.6.8.55          | 2191b464-58be-423d-9863-ce9c0397fc67 | 2012-05-27 11:10:06 |
9|  6 | i-2-6-VM      | 10.6.8.56          | c5be506a-aaae-475a-beb7-e6af2a33c8d3 | 2012-05-28 02:07:55 |

下面我們采用Redis作為數(shù)據(jù)庫,首先需要將關(guān)系型數(shù)據(jù)轉(zhuǎn)化為Key/Value數(shù)據(jù)。
可采用如下的方式來實現(xiàn):
Key --> 表名:主鍵值:列名
Value --> 列值

使用冒號作為分隔符,目前算是一個不成文的規(guī)矩。例如工具php-admin for redis就是默認以冒號分割的。
下面我以前五行數(shù)據(jù)為例,數(shù)據(jù)轉(zhuǎn)化的命令如下:

01SET vm_instance:1:instance_name s-1-VM
02SET vm_instance:2:instance_name v-2-VM
03SET vm_instance:4:instance_name r-4-VM
04SET vm_instance:5:instance_name i-2-5-VM
05SET vm_instance:6:instance_name i-2-6-VM
06 
07SET vm_instance:1:uuid 8c252255-82b8-4934-830e-0573cc9e0a1c
08SET vm_instance:2:uuid 1aae6ab9-73cb-46e3-aafb-985f6a143a08
09SET vm_instance:4:uuid 5520f0e9-4c5a-4599-be5c-0ea74b59d6dd
10SET vm_instance:5:uuid 2191b464-58be-423d-9863-ce9c0397fc67
11SET vm_instance:6:uuid c5be506a-aaae-475a-beb7-e6af2a33c8d3
12 
13SET vm_instance:1:private_ip_address 10.6.59.6
14SET vm_instance:2:private_ip_address 10.6.88.209
15SET vm_instance:4:private_ip_address 169.254.1.42
16SET vm_instance:5:private_ip_address 10.6.8.55
17SET vm_instance:6:private_ip_address 10.6.8.56
18 
19SET vm_instance:1:created "2012-05-27 04:06:54"
20SET vm_instance:2:created "2012-05-27 04:06:54"
21SET vm_instance:4:created "2012-05-27 10:45:42"
22SET vm_instance:5:created "2012-05-27 11:10:06"
23SET vm_instance:6:created "2012-05-28 02:07:55"

后面在大數(shù)據(jù)量生成時我將通過腳本來實現(xiàn)。
這樣在已知主鍵的情況下,通過GET,SET就可以獲得或修改instance_name,private_ip_address等屬性了。

一般的用戶是無法知道自己的id的,只知道自己的instance_name,所以增加一個從instance_name到id的映射是個不錯的注意。

1SET vm_instance:s-1-VM:id 1
2SET vm_instance:v-2-VM:id 2
3SET vm_instance:r-4-VM:id 4
4SET vm_instance:i-2-5-VM:id 5
5SET vm_instance:i-2-6-VM:id 6

這樣,就可以通過instance_name來方便的查找所需的值了,如下所示:

1redis 127.0.0.1:6379> GET vm_instance:r-4-VM:id
2"4"
3redis 127.0.0.1:6379> GET vm_instance:4:private_ip_address
4"169.254.1.42"
5 
6redis 127.0.0.1:6379> GET vm_instance:i-2-5-VM:id
7"5"
8redis 127.0.0.1:6379> GET vm_instance:5:created
9"2012-05-27 11:10:06"

瀏覽一下當(dāng)前所有的KEY數(shù)據(jù):

01redis 127.0.0.1:6379> KEYS *
02 1) "vm_instance:r-4-VM:id"
03 2) "vm_instance:v-2-VM:id"
04 3) "vm_instance:1:instance_name"
05 4) "vm_instance:i-2-5-VM:id"
06 5) "vm_instance:2:instance_name"
07 6) "vm_instance:i-2-6-VM:id"
08 7) "vm_instance:1:uuid"
09 8) "vm_instance:1:created"
10 9) "vm_instance:4:instance_name"
1110) "vm_instance:2:uuid"
1211) "vm_instance:1:private_ip_address"
1312) "vm_instance:2:created"
1413) "vm_instance:5:instance_name"
1514) "vm_instance:2:private_ip_address"
1615) "vm_instance:6:instance_name"
1716) "vm_instance:4:uuid"
1817) "vm_instance:4:created"
1918) "vm_instance:5:uuid"
2019) "vm_instance:4:private_ip_address"
2120) "vm_instance:5:created"
2221) "vm_instance:5:private_ip_address"
2322) "vm_instance:6:uuid"
2423) "vm_instance:s-1-VM:id"
2524) "vm_instance:6:created"
2625) "vm_instance:6:private_ip_address"

下面,我將通過腳本來生成大量的數(shù)據(jù)(100萬條)。
首先,清除所有的數(shù)據(jù):

1redis 127.0.0.1:6379> FLUSHALL

查看當(dāng)前的內(nèi)存耗用:

01redis 127.0.0.1:6379> INFO
02redis_version:2.4.17
03redis_git_sha1:00000000
04redis_git_dirty:0
05arch_bits:64
06multiplexing_api:epoll
07gcc_version:4.4.5
08process_id:1326
09run_id:362c470ccf38b87aa955d1e1e447f58522a271c6
10uptime_in_seconds:54193
11uptime_in_days:0
12lru_clock:643288
13used_cpu_sys:571.23
14used_cpu_user:72.46
15used_cpu_sys_children:46.74
16used_cpu_user_children:162.62
17connected_clients:1
18connected_slaves:1
19client_longest_output_list:0
20client_biggest_input_buf:0
21blocked_clients:0
22used_memory:735864
23used_memory_human:718.62K
24used_memory_rss:6701056
25used_memory_peak:236219680
26used_memory_peak_human:225.28M
27mem_fragmentation_ratio:9.11
28mem_allocator:jemalloc-3.0.0
29loading:0
30aof_enabled:0
31changes_since_last_save:30
32bgsave_in_progress:0
33last_save_time:1348610141
34bgrewriteaof_in_progress:0
35total_connections_received:1812645
36total_commands_processed:5430976
37expired_keys:0
38evicted_keys:0
39keyspace_hits:18
40keyspace_misses:9
41pubsub_channels:0
42pubsub_patterns:0
43latest_fork_usec:1144
44vm_enabled:0
45role:master
46slave0:10.6.1.144,6379,online

內(nèi)存的耗用非常少,僅為718.62K (735864)。

下面的Shell腳本將生成100萬條數(shù)據(jù)(20萬*5):
dongguo@redis:~/shell$ vim redis-cli-generate.sh

01#!/bin/bash
02 
03REDISCLI="redis-cli -a slavepass -n 2 SET"
04ID=1
05 
06while(($ID<200000))
07do
08  INSTANCE_NAME="i-2-$ID-VM"
09  UUID=`cat /proc/sys/kernel/random/uuid`
10  PRIVATE_IP_ADDRESS=10.`echo "$RANDOM % 255 + 1" | bc`.`echo "$RANDOM % 255 + 1" | bc`.`echo "$RANDOM % 255 + 1" | bc`\
11  CREATED=`date "+%Y-%m-%d %H:%M:%S"`
12 
13  $REDISCLI vm_instance:$ID:instance_name $INSTANCE_NAME
14  $REDISCLI vm_instance:$ID:uuid $UUID
15  $REDISCLI vm_instance:$ID:private_ip_address $PRIVATE_IP_ADDRESS
16  $REDISCLI vm_instance:$ID:created $CREATED
17 
18  $REDISCLI vm_instance:$INSTANCE_NAME:id $ID
19 
20  ID=$(($ID+1))
21done

創(chuàng)建一個screen終端,將腳本放到終端中后臺執(zhí)行是個不錯的注意。
dongguo@redis:~/shell$ screen -dmS redis
dongguo@redis:~/shell$ screen -r redis
dongguo@redis:~/shell$ ./redis-cli-generate.sh
同時按下Ctrl+AD三個按鈕退出終端。

等待大約2個小時以后,數(shù)據(jù)終于寫入完成(因為是虛擬機環(huán)境,所以才等這么久)。

查看一下當(dāng)前的內(nèi)存開銷:

01redis 127.0.0.1:6379> info
02redis_version:2.4.17
03redis_git_sha1:00000000
04redis_git_dirty:0
05arch_bits:64
06multiplexing_api:epoll
07gcc_version:4.4.5
08process_id:1326
09run_id:362c470ccf38b87aa955d1e1e447f58522a271c6
10uptime_in_seconds:60658
11uptime_in_days:0
12lru_clock:643935
13used_cpu_sys:858.31
14used_cpu_user:105.16
15used_cpu_sys_children:58.20
16used_cpu_user_children:190.09
17connected_clients:1
18connected_slaves:1
19client_longest_output_list:0
20client_biggest_input_buf:0
21blocked_clients:0
22used_memory:130548280
23used_memory_human:124.50M
24used_memory_rss:134524928
25used_memory_peak:236219680
26used_memory_peak_human:225.28M
27mem_fragmentation_ratio:1.03
28mem_allocator:jemalloc-3.0.0
29loading:0
30aof_enabled:0
31changes_since_last_save:1
32bgsave_in_progress:0
33last_save_time:1348616616
34bgrewriteaof_in_progress:0
35total_connections_received:2863881
36total_commands_processed:8584847
37expired_keys:0
38evicted_keys:0
39keyspace_hits:31
40keyspace_misses:10
41pubsub_channels:0
42pubsub_patterns:0
43latest_fork_usec:19620
44vm_enabled:0
45role:master
46slave0:10.6.1.144,6379,online
47db2:keys=999995,expires=0

目前的內(nèi)存耗用為124.50M (130548280)。

在數(shù)據(jù)生成之后,接下來才是本文的重點,即參考Instagram的例子做一些優(yōu)化的實踐。

首先,讓我們確認現(xiàn)在的內(nèi)存開銷:
124.50M (130548280)

第一個優(yōu)化點很明顯也很簡單,可以把所有key值前面相同的vm_instance:去掉,也就是之前定義的表名,將其放置在一個獨立的數(shù)據(jù)庫(這里選擇2號)中,避免其他的數(shù)據(jù)混進來就可以了。
這里就立刻節(jié)省了12個字節(jié)的開銷,然后剩下的繼續(xù)設(shè)法減少開銷,可以將instance_name優(yōu)化為name,private_ip_address優(yōu)化為ip,這樣就累積節(jié)省了12+9+16=37個字節(jié)的開銷。

初步優(yōu)化過后的數(shù)據(jù)如下:

1SET 1:name i-2-1-VM
2SET 1:uuid 8c252255-82b8-4934-830e-0573cc9e0a1c
3SET 1:ip 10.6.59.6
4SET 1:created "2012-05-27 04:06:54"
5SET i-2-1-VM:id 1

通過腳本導(dǎo)入優(yōu)化過后的數(shù)據(jù),并做內(nèi)存開銷上的對比。
dongguo@redis:~/shell$ cat redis-cli-generate_2.sh

01#!/bin/bash
02 
03REDISCLI="redis-cli -a slavepass -n 2 SET"
04ID=1
05 
06while(($ID<200000))
07do
08  INSTANCE_NAME="i-2-$ID-VM"
09  UUID=`cat /proc/sys/kernel/random/uuid`
10  PRIVATE_IP_ADDRESS=10.`echo "$RANDOM % 255 + 1" | bc`.`echo "$RANDOM % 255 + 1" | bc`.`echo "$RANDOM % 255 + 1" | bc`\
11  CREATED=`date "+%Y-%m-%d %H:%M:%S"`
12 
13  $REDISCLI $ID:name "$INSTANCE_NAME"
14  $REDISCLI $ID:uuid "$UUID"
15  $REDISCLI $ID:ip "$PRIVATE_IP_ADDRESS"
16  $REDISCLI $ID:created "$CREATED"
17 
18  $REDISCLI $INSTANCE_NAME:id "$ID"
19 
20  ID=$(($ID+1))
21done

清除數(shù)據(jù),用腳本導(dǎo)入新的數(shù)據(jù)。

1redis 127.0.0.1:6379> FLUSHALL

dongguo@redis:~/shell$ screen -r redis
dongguo@redis:~/shell$ ./redis-cli-generate_2.sh
同時按下Ctrl+AD三個按鈕退出終端。

等待大約2個小時以后,數(shù)據(jù)再次寫入完成。

查看內(nèi)存開銷:

01redis 127.0.0.1:6379> info
02redis_version:2.4.17
03redis_git_sha1:00000000
04redis_git_dirty:0
05arch_bits:64
06multiplexing_api:epoll
07gcc_version:4.4.5
08process_id:1326
09run_id:362c470ccf38b87aa955d1e1e447f58522a271c6
10uptime_in_seconds:65449
11uptime_in_days:0
12lru_clock:644414
13used_cpu_sys:1140.08
14used_cpu_user:139.45
15used_cpu_sys_children:66.33
16used_cpu_user_children:211.75
17connected_clients:1
18connected_slaves:1
19client_longest_output_list:0
20client_biggest_input_buf:0
21blocked_clients:0
22used_memory:117601616
23used_memory_human:112.15M
24used_memory_rss:121319424
25used_memory_peak:236219680
26used_memory_peak_human:225.28M
27mem_fragmentation_ratio:1.03
28mem_allocator:jemalloc-3.0.0
29loading:0
30aof_enabled:0
31changes_since_last_save:2795
32bgsave_in_progress:0
33last_save_time:1348621199
34bgrewriteaof_in_progress:0
35total_connections_received:3863886
36total_commands_processed:11584940
37expired_keys:0
38evicted_keys:0
39keyspace_hits:41
40keyspace_misses:12
41pubsub_channels:0
42pubsub_patterns:0
43latest_fork_usec:2101
44vm_enabled:0
45role:master
46slave0:10.6.1.144,6379,online
47db2:keys=999995,expires=0

所占內(nèi)存大小為112.15M (117601616)。

結(jié)論:
通過對字節(jié)數(shù)的優(yōu)化,內(nèi)存從124.50M (130548280) 減少到了 112.15M (117601616)。
比例為 1 - (117601616/130548280) = 1 - 0.9008285363851596 = 0.0991714636148404,即節(jié)省了9%的內(nèi)存,感覺效果并不是很明顯。

這個結(jié)果倒也不出乎以外,因為Instagram將內(nèi)存得到了顯著提升,是在使用了Hash結(jié)構(gòu)對數(shù)據(jù)進行存儲之后。

具體的做法呢就是將數(shù)據(jù)分段,每一段使用一個Hash結(jié)構(gòu)來存儲,這一點在String結(jié)構(gòu)里是不存在的。
據(jù)稱經(jīng)過一些開發(fā)者們的實驗,將hash-zipmap-max-entries設(shè)置為1000時,性能比較好,超過1000后HSET命令就會導(dǎo)致CPU消耗變得非常大。

于是我們可以考慮將數(shù)據(jù)做成如下結(jié)構(gòu):

01redis 127.0.0.1:6379> GET 63233:name
02i-2-63233-VM
03 
04redis 127.0.0.1:6379> HSET 63:name 233 i-2-63233-VM
05redis 127.0.0.1:6379> HGET 63:name 233
06i-2-63233-VM
07 
08redis 127.0.0.1:6379> get 63233:uuid
09"556caf0f-3e6a-4b4f-a2d2-165144edaa5f"
10 
11redis 127.0.0.1:6379> HGET 63:uuid 233
12"556caf0f-3e6a-4b4f-a2d2-165144edaa5f"

將4位數(shù)以上的ID值轉(zhuǎn)換為Hash結(jié)構(gòu)的Key值,保證每個Hash內(nèi)部只包含3位的Key,也就是1000個。
對4位數(shù)以下的處理呢就很簡單了,全部把他們放到ID為0的key值中。

對應(yīng)的腳本如下,重新設(shè)計數(shù)據(jù),采用Hash結(jié)構(gòu)來存儲:
dongguo@redis:~/shell$ cat redis-cli-generate_3.sh

01#!/bin/bash
02 
03REDISCLI="redis-cli -a slavepass -n 2 HSET"
04ID=1
05 
06while(($ID<1000))
07do
08  INSTANCE_NAME="i-2-$ID-VM"
09  UUID=`cat /proc/sys/kernel/random/uuid`
10  PRIVATE_IP_ADDRESS=10.`echo "$RANDOM % 255 + 1" | bc`.`echo "$RANDOM % 255 + 1" | bc`.`echo "$RANDOM % 255 + 1" | bc`
11  CREATED=`date "+%Y-%m-%d %H:%M:%S"`
12 
13  $REDISCLI 0:name $ID "$INSTANCE_NAME"
14  $REDISCLI 0:uuid $ID "$UUID"
15  $REDISCLI 0:ip $ID "$PRIVATE_IP_ADDRESS"
16  $REDISCLI 0:created $ID "$CREATED"
17 
18  $REDISCLI i-2-0:id $ID-VM $ID
19 
20  ID=$(($ID+1))
21done
22 
23while(($ID<200000))
24do
25  INSTANCE_NAME="i-2-$ID-VM"
26  UUID=`cat /proc/sys/kernel/random/uuid`
27  PRIVATE_IP_ADDRESS=10.`echo "$RANDOM % 255 + 1" | bc`.`echo "$RANDOM % 255 + 1" | bc`.`echo "$RANDOM % 255 + 1" | bc`
28  CREATED=`date "+%Y-%m-%d %H:%M:%S"`
29 
30  LENGTH=`expr length $ID`
31  LENGTHCUT=`expr $LENGTH - 3`
32  LENGTHEND=`expr $LENGTHCUT + 1`
33 
34  VALUE1=`echo $ID | awk '{print substr($1,1,"'$LENGTHCUT'")}'`
35  VALUE2=`echo $ID | awk '{print substr($1,"'$LENGTHEND'",3)}'`
36 
37  $REDISCLI $VALUE1:name $VALUE2 "$INSTANCE_NAME"
38  $REDISCLI $VALUE1:uuid $VALUE2 "$UUID"
39  $REDISCLI $VALUE1:ip $VALUE2 "$PRIVATE_IP_ADDRESS"
40  $REDISCLI $VALUE1:created $VALUE2 "$CREATED"
41 
42  $REDISCLI i-2-$VALUE1:id $VALUE2-VM $ID
43 
44  ID=$(($ID+1))
45done

清除數(shù)據(jù):

1redis 127.0.0.1:6379> FLUSHALL

停止Redis服務(wù)器,以便修改配置文件參數(shù):
dongguo@redis:~/shell$ sudo /etc/init.d/redis stop
Stopping ...
Redis stopped.

修改配置文件參數(shù):
dongguo@redis:~/shell$ sudo vim /opt/redis/etc/redis_6379.conf

1hash-max-zipmap-entries 1000

用腳本導(dǎo)入新的數(shù)據(jù)
dongguo@redis:~/shell$ screen -r redis
dongguo@redis:~/shell$ ./redis-cli-generate_2.sh
同時按下Ctrl+AD三個按鈕退出終端。

等待大約2個小時以后,數(shù)據(jù)再次寫入完成。

激動人心的時刻就要到來了。
查看內(nèi)存開銷:

01redis 127.0.0.1:6379> info
02redis_version:2.4.17
03redis_git_sha1:00000000
04redis_git_dirty:0
05arch_bits:64
06multiplexing_api:epoll
07gcc_version:4.4.5
08process_id:31354
09run_id:35f282a72a80f2a82c13c89ba78b1b1d1281ae47
10uptime_in_seconds:6064
11uptime_in_days:0
12lru_clock:645106
13used_cpu_sys:300.16
14used_cpu_user:47.59
15used_cpu_sys_children:3.14
16used_cpu_user_children:4.49
17connected_clients:1
18connected_slaves:1
19client_longest_output_list:0
20client_biggest_input_buf:0
21blocked_clients:0
22used_memory:27022992
23used_memory_human:25.77M
24used_memory_rss:29540352
25used_memory_peak:27022968
26used_memory_peak_human:25.77M
27mem_fragmentation_ratio:1.09
28mem_allocator:jemalloc-3.0.0
29loading:0
30aof_enabled:0
31changes_since_last_save:0
32bgsave_in_progress:0
33last_save_time:1348628119
34bgrewriteaof_in_progress:0
35total_connections_received:1000026
36total_commands_processed:3000338
37expired_keys:0
38evicted_keys:0
39keyspace_hits:14
40keyspace_misses:14
41pubsub_channels:0
42pubsub_patterns:0
43latest_fork_usec:1679
44vm_enabled:0
45role:master
46slave0:10.6.1.144,6379,online
47db2:keys=1000,expires=0

所占內(nèi)存大小為25.77M (27022992)。

結(jié)論:
使用HASH結(jié)構(gòu)25.77M (27022992)和使用String結(jié)構(gòu)112.15M (117601616) 相比,節(jié)省內(nèi)存為 1 - (27022992/117601616) = 1 - 0.229784189360119 = 0.770215810639881 。
節(jié)省了 77% 的內(nèi)存。

優(yōu)化結(jié)果果然十分顯著,由此看來,我們在Redis中,通過采用HASH結(jié)構(gòu)來存儲數(shù)據(jù),和直接使用String結(jié)構(gòu)相比,可以十分有效的優(yōu)化內(nèi)存的占用。

目前公司的線上數(shù)據(jù)大部分都采用了String結(jié)構(gòu),且String中的內(nèi)容是經(jīng)過加密過后的JSON數(shù)據(jù)。

我的想法是,可以嘗試通過對現(xiàn)有的key進行修改或再次設(shè)計,將數(shù)據(jù)存儲到HASH結(jié)構(gòu)中,來實現(xiàn)對內(nèi)存占用的優(yōu)化。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
適用于分布式唯一標識碼的生成算法有哪些?
Manage bare metal nodes
python 阿里云收集服務(wù)器性能指標的python腳本
掃碼登錄實現(xiàn)原理
諧云課堂 | 一文詳解分布式改造理論與實戰(zhàn)
分布式系統(tǒng)ID生成辦法
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服