Maison  >  Article  >  développement back-end  >  La merveilleuse utilisation du bitmap dans Redis

La merveilleuse utilisation du bitmap dans Redis

高洛峰
高洛峰original
2017-02-09 10:30:321440parcourir

Dans Redis, nous utilisons souvent des commandes set, get et autres. Si vous faites attention, avez-vous remarqué qu'il existe plusieurs commandes similaires appelées setbit et getbit.

Qu'est-ce que BitMap ?

Il utilise un bit pour représenter la valeur ou le statut correspondant à un élément, et la clé est l'élément correspondant lui-même. Nous savons que 8 bits peuvent former un octet, donc le bitmap lui-même permettra d'économiser considérablement de l'espace de stockage.

BitMap dans Redis

Redis a ajouté plusieurs commandes liées au bitmap telles que setbit, getbit, bitcount et ainsi de suite depuis la version 2.2.0. Bien qu'il s'agisse d'une nouvelle commande, aucun nouveau type de données n'est ajouté, car les commandes telles que setbit ne sont que des extensions de set.

introduction à la commande setbit

CommandeSETBIT key offset value
ComplexitéO(1)
Définissez ou effacez la valeur binaire (chaîne) de la clé au décalage (uniquement 0 ou 1).

Occupation de l'espace et temps nécessaire pour allouer de l'espace pour la première fois

Sur un MacBook Pro 2010, le décalage est de 2 ^ 32-1 (512 Mo alloués) prend environ 300 ms, et le le décalage est de 2 ^ 30-1 (128 Mo alloués) prend environ 80 ms, le décalage 2 ^ 28-1 (32 Mo alloués) prend environ 30 ms, le décalage 2 ^ 26-1 (8 Mo alloués) prend 8 ms.
La formule approximative de calcul de l'occupation de l'espace est : ($offset/8/1024/1024)MB

Scénario d'utilisation 1 : connexion de l'utilisateur

De nombreux sites Web proposent des fonctions de connexion (non prises en compte ici) La mise en œuvre des données est importante) et doit afficher le statut d'enregistrement au cours du mois dernier. Que devons-nous faire si un bitmap est utilisé ? Le code se dévoile en un mot !

<?php
$redis = new Redis();
$redis->connect('127.0.0.1');


//用户uid
$uid = 1;

//记录有uid的key
$cacheKey = sprintf("sign_%d", $uid);

//开始有签到功能的日期
$startDate = '2017-01-01';

//今天的日期
$todayDate = '2017-01-21';

//计算offset
$startTime = strtotime($startDate);
$todayTime = strtotime($todayDate);
$offset = floor(($todayTime - $startTime) / 86400);

echo "今天是第{$offset}天" . PHP_EOL;

//签到
//一年一个用户会占用多少空间呢?大约365/8=45.625个字节,好小,有木有被惊呆?
$redis->setBit($cacheKey, $offset, 1);

//查询签到情况
$bitStatus = $redis->getBit($cacheKey, $offset);
echo 1 == $bitStatus ? '今天已经签到啦' : '还没有签到呢';
echo PHP_EOL;

//计算总签到次数
echo $redis->bitCount($cacheKey) . PHP_EOL;

/**
* 计算某段时间内的签到次数
* 很不幸啊,bitCount虽然提供了start和end参数,但是这个说的是字符串的位置,而不是对应"位"的位置
* 幸运的是我们可以通过get命令将value取出来,自己解析。并且这个value不会太大,上面计算过一年一个用户只需要45个字节
* 给我们的网站定一个小目标,运行30年,那么一共需要1.31KB(就问你屌不屌?)
*/
//这是个错误的计算方式
echo $redis->bitCount($cacheKey, 0, 20) . PHP_EOL;

Scénario d'utilisation 2 : compter les utilisateurs actifs

Utiliser le temps comme clé de cache, puis l'ID utilisateur est décalé S'il est actif ce jour-là, définissez-le sur 1
Ensuite. comment dois-je calculer un certain nombre de jours/mois/années d'utilisateurs actifs (provisoirement convenu, un seul jour en ligne pendant la période statistique est appelé actif), veuillez exécuter une commande redis
Commande BITOP operation destkey key [key ...]
Explication : Pour un ou plusieurs Effectuez des opérations sur les bits sur une clé de chaîne contenant des bits binaires et enregistrez le résultat dans destkey.
Remarque : la commande BITOP prend en charge n'importe quel paramètre des quatre opérations AND, OR, NOT et /1024/1024=6MB

//日期对应的活跃用户

$data = array(

'2017-01-10' => array(1,2,3,4,5,6,7,8,9,10),

'2017-01-11' => array(1,2,3,4,5,6,7,8),

'2017-01-12' => array(1,2,3,4,5,6),

'2017-01-13' => array(1,2,3,4),

'2017-01-14' => array(1,2)

);



//批量设置活跃状态

foreach($data as $date=>$uids) {

$cacheKey = sprintf("stat_%s", $date);

foreach($uids as $uid) {

$redis->setBit($cacheKey, $uid, 1);

}

}



$redis->bitOp('AND', 'stat', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-12') . PHP_EOL;

//总活跃用户:6

echo "总活跃用户:" . $redis->bitCount('stat') . PHP_EOL;



$redis->bitOp('AND', 'stat1', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-14') . PHP_EOL;

//总活跃用户:2

echo "总活跃用户:" . $redis->bitCount('stat1') . PHP_EOL;



$redis->bitOp('AND', 'stat2', 'stat_2017-01-10', 'stat_2017-01-11') . PHP_EOL;

//总活跃用户:8

echo "总活跃用户:" . $redis->bitCount('stat2') . PHP_EOL;
Scénario d'utilisation trois : statut de l'utilisateur en ligne

J'ai développé un projet il y a quelque temps, et l'autre partie m'a fourni une interface pour demander si l'utilisateur actuel est en ligne. Je ne sais pas comment l'autre partie fait, alors j'y ai pensé moi-même. L'utilisation du bitmap est une méthode efficace et peu encombrante. Elle ne nécessite qu'une seule clé, puis l'ID utilisateur est décalé. est défini sur 1, et s'il n'est pas en ligne, il est défini sur 1. 0. Comme dans le scénario ci-dessus, un utilisateur de 5 000 W n'a besoin que de 6 Mo d'espace.

En fait, BitMap peut être utilisé dans de nombreux scénarios (bien sûr, il sera soumis à certaines restrictions), et les idées peuvent continuer à se propager~ Les amis sont invités à me laisser un message pour en discuter~

//批量设置在线状态
$uids = range(1, 500000);

foreach($uids as $uid) {

$redis->setBit('online', $uid, $uid % 2);

}

//一个一个获取状态

$uids = range(1, 500000);

$startTime = microtime(true);

foreach($uids as $uid) {

echo $redis->getBit('online', $uid) . PHP_EOL;

}

$endTime = microtime(true);

//在我的电脑上,获取50W个用户的状态需要25秒

echo "total:" . ($endTime - $startTime) . "s";




/**

* 对于批量的获取,上面是一种效率低的办法,实际可以通过get获取到value,然后自己计算

* 具体计算方法改天再写吧,之前写的代码找不见了。。。

*/

Dans Redis, nous utilisons souvent des commandes telles que set et get Si vous faites attention, avez-vous remarqué qu'il existe plusieurs commandes similaires appelées setbit et getbit.

Qu'est-ce que BitMap ?

représente la valeur ou l'état d'un élément via un bit, et la clé est l'élément correspondant lui-même. Nous savons que 8 bits peuvent former un octet, donc le bitmap lui-même permettra d'économiser considérablement de l'espace de stockage.

BitMap dans Redis

Redis a ajouté plusieurs commandes liées au bitmap telles que

,

,

et ainsi de suite depuis la version 2.2.0. Bien qu'il s'agisse d'une nouvelle commande, aucun nouveau type de données n'est ajouté, car les commandes telles que setbit ne sont que des extensions de getbit. bitcountsetbitintroduction à la commande setbitset

Commande

Complexité

SETBIT key offset valueDéfinissez ou effacez la valeur binaire (chaîne) de la clé au décalage (uniquement 0 ou 1).
O(1)Occupation de l'espace et temps nécessaire pour allouer de l'espace pour la première fois

Sur un MacBook Pro 2010, le décalage est de 2 ^ 32-1 (512 Mo alloués) prend environ 300 ms, et le le décalage est de 2 ^ 30-1 (128 Mo alloués) prend environ 80 ms, le décalage 2 ^ 28-1 (32 Mo alloués) prend environ 30 ms, le décalage 2 ^ 26-1 (8 Mo alloués) prend 8 ms.

La formule approximative de calcul de l'occupation de l'espace est :


Scénario d'utilisation 1 : connexion de l'utilisateur($offset/8/1024/1024)MB

De nombreux sites Web proposent des fonctions de connexion (non prises en compte ici) La mise en œuvre des données est importante) et doit afficher le statut d'enregistrement au cours du mois dernier. Comment devrions-nous procéder si un bitmap est utilisé ? Le code se dévoile en un mot !

<?php
$redis = new Redis();
$redis->connect('127.0.0.1');


//用户uid
$uid = 1;

//记录有uid的key
$cacheKey = sprintf("sign_%d", $uid);

//开始有签到功能的日期
$startDate = '2017-01-01';

//今天的日期
$todayDate = '2017-01-21';

//计算offset
$startTime = strtotime($startDate);
$todayTime = strtotime($todayDate);
$offset = floor(($todayTime - $startTime) / 86400);

echo "今天是第{$offset}天" . PHP_EOL;

//签到
//一年一个用户会占用多少空间呢?大约365/8=45.625个字节,好小,有木有被惊呆?
$redis->setBit($cacheKey, $offset, 1);

//查询签到情况
$bitStatus = $redis->getBit($cacheKey, $offset);
echo 1 == $bitStatus ? '今天已经签到啦' : '还没有签到呢';
echo PHP_EOL;

//计算总签到次数
echo $redis->bitCount($cacheKey) . PHP_EOL;

/**
* 计算某段时间内的签到次数
* 很不幸啊,bitCount虽然提供了start和end参数,但是这个说的是字符串的位置,而不是对应"位"的位置
* 幸运的是我们可以通过get命令将value取出来,自己解析。并且这个value不会太大,上面计算过一年一个用户只需要45个字节
* 给我们的网站定一个小目标,运行30年,那么一共需要1.31KB(就问你屌不屌?)
*/
//这是个错误的计算方式
echo $redis->bitCount($cacheKey, 0, 20) . PHP_EOL;

Scénario d'utilisation 2 : compter les utilisateurs actifs

Utiliser le temps comme clé de cache, puis l'ID utilisateur est décalé S'il est actif ce jour-là, définissez-le sur 1
Alors, comment dois-je le faire. calculer certains jours/ Quant aux utilisateurs actifs en mois/année (pour le moment, il est convenu qu'en ligne un seul jour dans la période statistique est dit actif), veuillez utiliser la commande redis suivante
commande BITOP operation destkey key [key ...]
Description : Enregistrez un ou plusieurs fichiers binaires. Effectuez des opérations binaires sur la clé de la chaîne de bits et enregistrez le résultat dans destkey.
Remarque : la commande BITOP prend en charge n'importe quel paramètre des quatre opérations AND, OR, NOT et /1024/1024=6MB

//日期对应的活跃用户

$data = array(

'2017-01-10' => array(1,2,3,4,5,6,7,8,9,10),

'2017-01-11' => array(1,2,3,4,5,6,7,8),

'2017-01-12' => array(1,2,3,4,5,6),

'2017-01-13' => array(1,2,3,4),

'2017-01-14' => array(1,2)

);



//批量设置活跃状态

foreach($data as $date=>$uids) {

$cacheKey = sprintf("stat_%s", $date);

foreach($uids as $uid) {

$redis->setBit($cacheKey, $uid, 1);

}

}



$redis->bitOp('AND', 'stat', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-12') . PHP_EOL;

//总活跃用户:6

echo "总活跃用户:" . $redis->bitCount('stat') . PHP_EOL;



$redis->bitOp('AND', 'stat1', 'stat_2017-01-10', 'stat_2017-01-11', 'stat_2017-01-14') . PHP_EOL;

//总活跃用户:2

echo "总活跃用户:" . $redis->bitCount('stat1') . PHP_EOL;



$redis->bitOp('AND', 'stat2', 'stat_2017-01-10', 'stat_2017-01-11') . PHP_EOL;

//总活跃用户:8

echo "总活跃用户:" . $redis->bitCount('stat2') . PHP_EOL;
Scénario d'utilisation trois : statut de l'utilisateur en ligne

J'ai développé un projet il y a quelque temps, et l'autre partie m'a fourni une interface pour demander si l'utilisateur actuel est en ligne. Je ne sais pas comment l'autre partie fait, alors j'y ai pensé moi-même. L'utilisation du bitmap est une méthode efficace et peu encombrante. Elle ne nécessite qu'une seule clé, puis l'ID utilisateur est décalé. est défini sur 1, et s'il n'est pas en ligne, il est défini sur 1. 0. Comme dans le scénario ci-dessus, un utilisateur de 5 000 W n'a besoin que de 6 Mo d'espace.

En fait, BitMap peut être utilisé dans de nombreux scénarios (bien sûr, il sera soumis à certaines restrictions), et les idées peuvent continuer à se propager~ Les amis sont invités à me laisser un message pour en discuter~

//批量设置在线状态
$uids = range(1, 500000);

foreach($uids as $uid) {

$redis->setBit('online', $uid, $uid % 2);

}

//一个一个获取状态

$uids = range(1, 500000);

$startTime = microtime(true);

foreach($uids as $uid) {

echo $redis->getBit('online', $uid) . PHP_EOL;

}

$endTime = microtime(true);

//在我的电脑上,获取50W个用户的状态需要25秒

echo "total:" . ($endTime - $startTime) . "s";




/**

* 对于批量的获取,上面是一种效率低的办法,实际可以通过get获取到value,然后自己计算

* 具体计算方法改天再写吧,之前写的代码找不见了。。。

*/
Plus de bitmaps dans Redis Pour les articles connexes, veuillez faire attention au site Web PHP chinois !

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn