亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

降低PHP Redis內(nèi)存占用

 更新時間:2017年03月23日 10:57:23   作者:壹葉隨風(fēng)  
本文主要介紹了降低PHP Redis內(nèi)存占用的方法。具有很好的參考價值。下面跟著小編一起來看下吧

1、降低redis內(nèi)存占用的優(yōu)點

 1、有助于減少創(chuàng)建快照和加載快照所用的時間

 2、提升載入AOF文件和重寫AOF文件時的效率

 3、縮短從服務(wù)器進行同步所需的時間

 4、無需添加額外的硬件就可以讓redis存貯更多的數(shù)據(jù)

2、短結(jié)構(gòu)

Redis為列表、集合、散列、有序集合提供了一組配置選項,這些選項可以讓redis以更節(jié)約的方式存儲較短的結(jié)構(gòu)。

2.1、ziplist壓縮列表(列表、散列、有續(xù)集和)

通常情況下使用的存儲方式

當列表、散列、有序集合的長度較短或者體積較小的時候,redis將會采用一種名為ziplist的緊湊存儲方式來存儲這些結(jié)構(gòu)。

ziplist是列表、散列、有序集合這三種不同類型的對象的一種非結(jié)構(gòu)化表示,它會以序列化的方式存儲數(shù)據(jù),這些序列化的數(shù)據(jù)每次被讀取的時候都需要進行解碼,每次寫入的時候也要進行編碼。

雙向列表與壓縮列表的區(qū)別:

為了了解壓縮列表比其他數(shù)據(jù)結(jié)構(gòu)更加節(jié)約內(nèi)存,我們以列表結(jié)構(gòu)為例進行深入研究。

典型的雙向列表

在典型雙向列表里面,每個值都都會有一個節(jié)點表示。每個節(jié)點都會帶有指向鏈表前一個節(jié)點和后一個節(jié)點的指針,以及一個指向節(jié)點包含的字符串值的指針。

每個節(jié)點包含的字符串值都會分為三部分進行存儲。包括字符串長度、字符串值中剩余可用字節(jié)數(shù)量、以空字符結(jié)尾的字符串本身。

例子:

假若一個某個節(jié)點存儲了’abc’字符串,在32位的平臺下保守估計需要21個字節(jié)的額外開銷(三個指針+兩個int+空字符即:3*4+2*4+1=21)

由例子可知存儲一個3字節(jié)字符串就需要付出至少21個字節(jié)的額外開銷。

ziplist

壓縮列表是由節(jié)點組成的序列,每個節(jié)點包含兩個長度和一個字符串。第一個長度記錄前一個節(jié)點的長度(用于對壓縮列表從后向前遍歷);第二個長度是記錄本當前點的長度;被存儲的字符串。

例子:

存儲字符串’abc’,兩個長度都可以用1字節(jié)來存儲,因此所帶來的額外開銷為2字節(jié)(兩個長度即1+1=2)

結(jié)論:

壓縮列表是通過避免存儲額外的指針和元數(shù)據(jù),從而達到降低額外的開銷。

配置:

#list
list-max-ziplist-entries 512 #表示允許包含的最大元素數(shù)量
list-max-ziplist-value 64  #表示壓縮節(jié)點允許存儲的最大體積
#hash         #當超過任一限制后,將不會使用ziplist方式進行存儲
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
#zset
zset-max-ziplist-entries 128
zset-max-ziplist-value 64

測試list:

1、建立test.php文件

#test.php
<?php
$redis=new Redis();
$redis->connect('192.168.95.11','6379');
for ($i=0; $i<512 ; $i++) 
{ 
  $redis->lpush('test-list',$i.'-test-list'); #往test-list推入512條數(shù)據(jù)
}
?>

此時的test-list中含有512條數(shù)據(jù),沒有超除配置文件中的限制

2、往test-list中再推入一條數(shù)據(jù)

此時test-list含有513條數(shù)據(jù),大于配置文件中限制的512條,索引將放棄ziplist存儲方式,采用其原來的linkedlist存儲方式

散列與有序集合同理。

2.2、intset整數(shù)集合(集合)

前提條件,集合中包含的所有member都可以被解析為十進制整數(shù)。

以有序數(shù)組的方式存儲集合不僅可以降低內(nèi)存消耗,還可以提升集合操作的執(zhí)行速度。

配置:

set-max-intset-entries  512   #限制集合中member個數(shù),超出則不采取intset存儲

測試:

建立test.php文件

#test.php
<?php
$redis=new Redis();
$redis->connect('192.168.95.11','6379');
for ($i=0; $i<512 ; $i++) 
{ 
  $redis->sadd('test-set',$i);  #給集合test-set插入512個member
}
?>

2.3、性能問題

不管列表、散列、有序集合、集合,當超出限制的條件后,就會轉(zhuǎn)換為更為典型的底層結(jié)構(gòu)類型。因為隨著緊湊結(jié)構(gòu)的體積不斷變大,操作這些結(jié)構(gòu)的速度將會變得越來越慢。

測試:

#將采用list進行代表性測試

測試思路:

1、在默認配置下往test-list推入50000條數(shù)據(jù),查看所需時間;接著在使用rpoplpush將test-list數(shù)據(jù)全部推入到新列表list-new中,查看所需時間

2、修改配置,list-max-ziplist-entries 100000,再執(zhí)行上面的同樣操作

3、對比時間,得出結(jié)論

默認配置下測試:

1、數(shù)據(jù),查看時間

#test1.php
<?php
header("content-type: text/html;charset=utf8;");
$redis=new Redis();
$redis->connect('192.168.95.11','6379');
$start=time();
for ($i=0; $i<50000 ; $i++) 
{ 
  $redis->lpush('test-list',$i.'-aaaassssssddddddkkk');
}
$end=time();
echo "插入耗時為:".($end-$start).'s';
?>

結(jié)果耗時4秒

2、執(zhí)行相應(yīng)命令,查看耗時

#test2.php
<?php
header("content-type: text/html;charset=utf8;");
$redis=new Redis();
$redis->connect('192.168.95.11','6379');
$start=time();
$num=0;
while($redis->rpoplpush('test-list','test-new'))
{
  $num+=1;
}
echo '執(zhí)行次數(shù)為:'.$num."<br/>";
$end=time();
echo "耗時為:".($end-$start).'s';
?>

更改配置文件下測試  

1、先修改配置文件

list-max-ziplist-entries 100000  #將這個值修改大一點,可以更好的凸顯對性能的影響

list-max-ziplist-value 64    #此值可不做修改

2、數(shù)據(jù)

執(zhí)行test1.php

結(jié)果為:耗時12s

3、執(zhí)行相應(yīng)命令,查看耗時

執(zhí)行test2.php

結(jié)果為:執(zhí)行次數(shù):50000,耗時12s

結(jié)論:

在本機中執(zhí)行測試50000條數(shù)據(jù)就相差8s,若在高并發(fā)下,長壓縮列表和大整數(shù)集合將起不到任何的優(yōu)化,反而使得性能降低。

3、片結(jié)構(gòu)

分片的本質(zhì)就是基于簡單的規(guī)則將數(shù)據(jù)劃分為更小的部分,然后根據(jù)數(shù)據(jù)所屬的部分來決定將數(shù)據(jù)發(fā)送到哪個位置上。很多數(shù)據(jù)庫使用這種技術(shù)來擴展存儲空間,并提高自己所能處理的負載量。

結(jié)合前面講到的,我們不難發(fā)現(xiàn)分片結(jié)構(gòu)對于redis的重要意義。因此我們需要在配置文件中關(guān)于ziplist以及intset的相關(guān)配置做出適當?shù)恼{(diào)整。

3.1、分片式散列

#ShardHash.class.php

<?php
class ShardHash
{
  private $redis=''; #存儲redis對象
  /**
  * @desc 構(gòu)造函數(shù)
  * 
  * @param $host string | redis主機
  * @param $port int  | 端口
  */
  public function __construct($host,$port=6379)
  {
    $this->redis=new Redis();
    $this->redis->connect($host,$port);
  } 
  /**
  * @desc 計算某key的分片ID
  *
  * @param $base string | 基礎(chǔ)散列
  * @param $key  string | 要存儲到分片散列里的鍵名
  * @param $total int  | 預(yù)計非數(shù)字分片總數(shù)
  * 
  * @return string | 返回分片鍵key
  */
  public function shardKey ($base,$key,$total)
  {
    if(is_numeric($key))
    {
      $shard_id=decbin(substr(bindec($key),0,5)); #取$key二進制高五位的十進制值
    }
    else
    {
      $shard_id=crc32($key)%$shards; #求余取模
    }
    return $base.'_'.$shard_id;
  }
  /**
  * @desc 分片式散列hset操作
  *
  * @param $base string | 基礎(chǔ)散列
  * @param $key  string | 要存儲到分片散列里的鍵名
  * @param $total int  | 預(yù)計元素總數(shù)
  * @param $value string/int | 值
  *
  * @return bool | 是否hset成功
  */
  public function shardHset($base,$key,$total,$value)
  {
    $shardKey=$this->shardKey($base,$key,$total);
    return $this->redis->hset($shardKey,$key,$value);
  }
  /**
  * @desc 分片式散列hget操作
  *
  * @param $base string | 基礎(chǔ)散列
  * @param $key  string | 要存儲到分片散列里的鍵名
  * @param $total int  | 預(yù)計元素總數(shù)
  *
  * @return string/false | 成功返回value
  */
  public function shardHget($base,$key,$total)
  {
    $shardKey=$this->shardKey($base,$key,$total);
    return $this->redis->hget($shardKey,$key);
  }
} 
$obj=new ShardHash('192.168.95.11');
echo $obj->shardHget('hash-','key',500);
?>

散列分片主要是根據(jù)基礎(chǔ)鍵以及散列包含的鍵計算出分片鍵ID,然后再與基礎(chǔ)鍵拼接成一個完整的分片鍵。在執(zhí)行hset與hget以及大部分hash命令時,都需要先將key(field)通過shardKey方法處理,得到分片鍵才能夠進行下一步操作。

3.2、分片式集合

如何構(gòu)造分片式集合才能夠讓它更節(jié)省內(nèi)存,性能更加強大呢?主要的思路就是,將集合里面的存儲的數(shù)據(jù)盡量在不改變其原有功能的情況下轉(zhuǎn)換成可以被解析為十進制的數(shù)據(jù)。根據(jù)前面所講到的,當集合中的所有成員都能夠被解析為十進制數(shù)據(jù)時,將會采用intset存儲方式,這不僅能夠節(jié)省內(nèi)存,而且還可以提高響應(yīng)的性能。

例子:

假若要某個大型網(wǎng)站需要存儲每一天的唯一用戶訪問量。那么就可以使用將用戶的唯一標識符轉(zhuǎn)化成十進制數(shù)字,再存入分片式set中。

#ShardSet.class.php

<?php
class ShardSet
{
  private $redis=''; #存儲redis對象
  /**
  * @desc 構(gòu)造函數(shù)
  * 
  * @param $host string | redis主機
  * @param $port int  | 端口
  */
  public function __construct($host,$port=6379)
  {
    $this->redis=new Redis();
    $this->redis->connect($host,$port);
  } 
  /**
  * @desc 根據(jù)基礎(chǔ)鍵以及散列包含的鍵計算出分片鍵
  *
  * @param $base string | 基礎(chǔ)散列
  * @param $key  string | 要存儲到分片散列里的鍵名
  * @param $total int  | 預(yù)計分片總數(shù)
  * 
  * @return string | 返回分片鍵key
  */
  public function shardKey ($base,$member,$total=512)
  {
    $shard_id=crc32($member)%$shards; #求余取模
    return $base.'_'.$shard_id;
  }
  /**
  * @desc 計算唯一用戶日訪問量
  * 
  * @param $member int | 用戶唯一標識符
  *
  * @return string | ok表示count加1 false表示用戶今天已經(jīng)訪問過不加1
  */
  public function count($member)
  {
    $shardKey=$this->shardKey('count',$member,$total=10); #$totla調(diào)小一點用于測試
    $exists=$this->redis->sismember($shardKey,$member); 
    if(!$exists)  #判斷member今天是否訪問過
    {
      $this->redis->sadd($shardKey,$member);
      $this->redis->incr('count');
      $ttl1=$this->redis->ttl('count');
      if($ttl1===-1)
        $this->redis->expireat('count',strtotime(date('Y-m-d 23:59:59'))); #設(shè)置過期時間
      $ttl2=$this->redis->ttl($shardKey);
      if($ttl2===-1)
      {
        $this->redis->expireat("$shardKey",strtotime(date('Y-m-d 23:59:59'))); #設(shè)置過期時間
        #echo $shardKey; #測試使用
      }
      #echo $shardKey;  #測試使用
      return 'ok';
    }
    return 'false';
  }
}
$str=substr(md5(uniqid()), 0, 8);  #取出前八位
#將$str作為客戶的唯一標識符
$str=hexdec($str);   #將16進制轉(zhuǎn)換為十進制
$obj=new ShardSet('192.168.95.11');
$obj->count($str);
?>

4、將信息打包轉(zhuǎn)換成存儲字節(jié)

結(jié)合前面所講的分片技術(shù),采用string分片結(jié)構(gòu)為大量連續(xù)的ID用戶存儲信息。

使用定長字符串,為每一個ID分配n個字節(jié)進行存儲相應(yīng)的信息。

接下來我們將采用存儲用戶國家、省份的例子進行講解:

假若某個用戶需要存儲中國、廣東省這兩個信息,采用utf8字符集,那么至少需要消耗5*3=15個字節(jié)。如果網(wǎng)站的用戶量大的話,這樣的做法將會占用很多資源。接下來我們采用的方法每個用戶僅僅只需要占用兩個字節(jié)就可以完成存儲信息。

具體思路步驟:

1、首先我們?yōu)閲?、以及各國家的省份信息建立相?yīng)的’信息表格’

2、將’信息表格’建好后,也意味著每個國家,省份都有相應(yīng)的索引號

3、看到這里大家應(yīng)該都想到了吧,對就是使用兩個索引作為用戶存儲的信息,不過需要注意的是我們還需要對這兩個索引進行相應(yīng)的處理

4、將索引當做ASCII碼,將其轉(zhuǎn)換為對應(yīng)ASCII(0~255)所指定的字符

5、使用前面所講的分片技術(shù),定長分片string結(jié)構(gòu),將用戶的存儲位置找出來(redis中一個string不能超過512M)

6、實現(xiàn)信息的寫入以及取出(getrange、setrange)

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關(guān)文章

最新評論