Redis學習之海量小資料的儲存詳解

來源:互聯網
上載者:User

標籤:

最近有個需求,需要儲存海量小資料,大概幾十億的規模,每個資料是6位的數字加一個32位的md5(16進位顯示)。因為資料很小,資料總量並不算大,我們計劃根據md5做分區,儲存到多個redis中,每個redis大概儲存1億的資料,純資料大概 (6+32)*10^9 = 3.8G ,這是redis資料庫很擅長存的量。

  1 快速載入資料到redis

  redis已經非常快了,高達 10w/s ,但面對億層級的資料,也需要將近20分鐘。如果使用pipeline的話,redis還可以更快,達到 40w/s ,5分鐘就可以輕鬆寫入1億資料。

  redis內建的 redis-cli 的 --pipe 參數可以實現快速載入資料,但是需要我們把資料轉成redis協議。 --pipe-timeout 參數設定為0,防止redis響應太晚redis-cli過早退出。下面例子中的pl指令碼就是拼redis協議的。但是pl的效能稍弱,還沒到redis的輸送量瓶頸,自己CPU先100%了,為此,使用20個進程,每個進程500萬資料,這樣redis的CPU使用率到了100%,資料載入可以在5分鐘內完成。

  我們用 ps -eo ’pid rss pmem cmd’ | grep redis 和redis的 info 查看redis的記憶體使用量。

  2 最直觀的儲存方式

  time head -n 5000000 data | ./redis-pipe-1.pl | redis-cli --pipe --pipe-timeout 0 redis-pipe-1.pl 最核心的是 print join("\r\n", "*3", ’$3’, "SET", ’$’.$keylen, $key, ’$1’, 1), "\r\n"; 其中key是6位元字加32位md5串,共38位。

  記憶體使用量情況

  5490 9033980 6.8 redis-server *:6379

  used_memory_human:8.45Gdb0:keys=100000000,expires=0,avg_ttl=0

  從記憶體使用量上看,8G左右,是預估3.8G的2倍多。因為redis的內部資料結構,1個指標就是8位,在加上小value,slab記憶體配置策略,2倍也沒有特別不正常。

  3 使用二進位儲存

  md5 本身是 16 位的unsigned char,為了轉成可見字元用了16進位顯示,變成了32位。本來想用base64 24位就可以了,後來覺得redis支援二進位,為啥不直接存16位的unsigned char。 在./redis-pipe-2.pl裡面把32位的16進位顯示改成了16位的資料

  my @chars = ();my $hex = "";foreach (split //, $md5) {

  $hex .= $_;

  if (length($hex) == 2) {

  push(@chars, chr(hex($hex)));

  $hex = "";

  }

  }

  $key = $appid . join("", @chars);

  $keylen = length($key);

  記憶體使用量情況

  12343 7437316 5.6 redis-server *:6379used_memory_human:6.96Gdb0:keys=100000000,expires=0,avg_ttl=0

  從記憶體使用量上看,減少1.5G左右,和預期差不多 16*10^9 = 1.6G

  到現在為止,是從資料本身來減少記憶體使用量。而根據分析redis自身的資料結構消耗佔了一半左右,怎麼減少redis資料結構的消耗呢?

  4 使用SET和HSET混合的資料群組織方式

  先看兩個很有意思的配置,是專門為小Hash做準備(使用HSET),當Hash中的條目小於512,並且每個value小於64個位元組時,Redis內部採用特殊的編碼方式,可以使記憶體平均節省5倍。

  hash-max-ziplist-entries 512hash-max-ziplist-value 64

  我們可以把key-value的結構拆解成key-smallhash這樣的結構來降低記憶體的使用

  my ($appid, $md5) = split /\s/, $line;my @chars = ();my $hex = "";foreach (split //, $md5) {

  $hex .= $_;

  if (length($hex) == 2) {

  push(@chars, chr(hex($hex)));

  $hex = "";

  }

  }

  my $hash = $appid . join("", @chars[0 .. 2]);my $hashlen = length($hash);

  my $key = join("", @chars[3 .. @chars-1]);my $keylen = length("$key");

  print join("\r\n", "*4", ’$4’, "HSET", ’$’.$hashlen, $hash, ’$’.$keylen, $key, ’$1’, 1), "\r\n";

  三個unsigned char大概是 2^24 = 16777216 如果有1億記錄的話,每個hash自身平均6個key-value

  記憶體使用量情況

  8593 4052120 3.0 redis-server *:6379

  used_memory_human:3.31Gdb0:keys=16733972,expires=0,avg_ttl=0

  記憶體使用量3.31G,比裸資料 (6+16)*10^9 = 2.2G 只多了50%左右。不僅是省記憶體,這種方式還有個優勢,記憶體佔用 不會隨著條目數線性增長 。因為最多16777216個條目,就算資料導了2億,也只是每個hash到平均12個左右。

  5 額外需要關注的問題

  1. 本來準備把6位的數字轉成4位的整數儲存,可以額外節省200M,後來放棄了,因為數字轉成int,各個語言的互通性有隱患。

  2. 我們的redis讀不是特別多,需要測試hash的壓縮儲存對效能的影響,但我估計沒影響,因為預設是開的。

來源:跬步blog

Redis學習之海量小資料的儲存詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.