在实际开发中,我们是否有遇到以下情况。
基于此,导致数据库宕机了。怎么来处理呢?
答案:做布隆过滤吧
布隆过滤:目标数据晒查出来的结果是:可能存在;一定不存在。
把集合中的每一个值按照提供的Hash算法算出对应的Hash值,然后将Hash值对数组长度取模后得到需要计入数组的索引值,并且将数组这个位置的值从0改成1。在判断一个元素是否存在于这个集合中时,你只需要将这个元素按照相同的算法计算出索引值,如果这个位置的值为1就认为这个元素极大可能在集合中,否则认为绝对不在集合中。
hash解析映射
1、先定义出hash算法。用于对数据进行hash计算
class BloomFilterHash
{
/**
* 由Justin Sobel编写的按位散列函数
*/
public function JSHash($string, $len = null)
{
$hash = 1315423911;
$len || $len = strlen($string);
for ($i=0; $i<$len; $i++) {
$hash ^= (($hash << 5) + ord($string[$i]) + ($hash >> 2));
}
return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF;
}
/**
* 该哈希算法基于AT&T贝尔实验室的Peter J. Weinberger的工作。
* Aho Sethi和Ulman编写的“编译器(原理,技术和工具)”一书建议使用采用此特定算法中的散列方法的散列函数。
*/
public function PJWHash($string, $len = null)
{
$bitsInUnsignedInt = 4 * 8; //(unsigned int)(sizeof(unsigned int)* 8);
$threeQuarters = ($bitsInUnsignedInt * 3) / 4;
$oneEighth = $bitsInUnsignedInt / 8;
$highBits = 0xFFFFFFFF << (int) ($bitsInUnsignedInt - $oneEighth);
$hash = 0;
$test = 0;
$len || $len = strlen($string);
for($i=0; $i<$len; $i++) {
$hash = ($hash << (int) ($oneEighth)) + ord($string[$i]); } $test = $hash & $highBits; if ($test != 0) { $hash = (($hash ^ ($test >> (int)($threeQuarters))) & (~$highBits));
}
return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF;
}
/**
* 类似于PJW Hash功能,但针对32位处理器进行了调整。它是基于UNIX的系统上的widley使用哈希函数。
*/
public function ELFHash($string, $len = null)
{
$hash = 0;
$len || $len = strlen($string);
for ($i=0; $i<$len; $i++) {
$hash = ($hash << 4) + ord($string[$i]); $x = $hash & 0xF0000000; if ($x != 0) { $hash ^= ($x >> 24);
}
$hash &= ~$x;
}
return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF;
}
/**
* 这个哈希函数来自Brian Kernighan和Dennis Ritchie的书“The C Programming Language”。
* 它是一个简单的哈希函数,使用一组奇怪的可能种子,它们都构成了31 .... 31 ... 31等模式,它似乎与DJB哈希函数非常相似。
*/
public function BKDRHash($string, $len = null)
{
$seed = 131; # 31 131 1313 13131 131313 etc..
$hash = 0;
$len || $len = strlen($string);
for ($i=0; $i<$len; $i++) {
$hash = (int) (($hash * $seed) + ord($string[$i]));
}
return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF;
}
/**
* 这是在开源SDBM项目中使用的首选算法。
* 哈希函数似乎对许多不同的数据集具有良好的总体分布。它似乎适用于数据集中元素的MSB存在高差异的情况。
*/
public function SDBMHash($string, $len = null)
{
$hash = 0;
$len || $len = strlen($string);
for ($i=0; $i<$len; $i++) {
$hash = (int) (ord($string[$i]) + ($hash << 6) + ($hash << 16) - $hash);
}
return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF;
}
/**
* 由Daniel J. Bernstein教授制作的算法,首先在usenet新闻组comp.lang.c上向世界展示。
* 它是有史以来发布的最有效的哈希函数之一。
*/
public function DJBHash($string, $len = null)
{
$hash = 5381;
$len || $len = strlen($string);
for ($i=0; $i<$len; $i++) {
$hash = (int) (($hash << 5) + $hash) + ord($string[$i]);
}
return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF;
}
/**
* Donald E. Knuth在“计算机编程艺术第3卷”中提出的算法,主题是排序和搜索第6.4章。
*/
public function DEKHash($string, $len = null)
{
$len || $len = strlen($string);
$hash = $len;
for ($i=0; $i<$len; $i++) {
$hash = (($hash << 5) ^ ($hash >> 27)) ^ ord($string[$i]);
}
return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF;
}
/**
* 参考 http://www.isthe.com/chongo/tech/comp/fnv/
*/
public function FNVHash($string, $len = null)
{
$prime = 16777619; //32位的prime 2^24 + 2^8 + 0x93 = 16777619
$hash = 2166136261; //32位的offset
$len || $len = strlen($string);
for ($i=0; $i<$len; $i++) {
$hash = (int) ($hash * $prime) % 0xFFFFFFFF;
$hash ^= ord($string[$i]);
}
return ($hash % 0xFFFFFFFF) & 0xFFFFFFFF;
}
}
2、抽象布隆过滤操作
namespace App\Components\BloomFilter\Core;
/**
* 布隆过滤
*/
abstract class BloomFilterCore
{
/**
* 增加
*
* @param string $bucket
* @param string $string
*/
abstract public function add(string $bucket,string $string) : void;
/**
* 效验
*
* @param string $string
* @return bool
*/
abstract public function exists(string $bucket,string $string) : bool;
}
3、布隆过滤封装实现
a、通用版 - redis
namespace App\Components\BloomFilter;
use App\Components\BloomFilter\Core\BloomFilterCore;
use App\Components\BloomFilter\Core\HashFunction;
use App\Components\BloomFilter\Core\HashFunctionType;
use App\Exceptions\AppException;
use Illuminate\Redis\RedisManager;
/**
* 布隆过滤 通用版
*/
class BloomFilterBase extends BloomFilterCore
{
/**
* bucket 名称
*
* @var string
*/
protected string $bucket = 'base';
/**
* hash 算法
*
* @var array
*/
protected array $hashFunction = [
HashFunctionType::BKDRHash,
HashFunctionType::SDBMHash,
HashFunctionType::JSHash
];
protected HashFunction $hashFunctionObj;
protected RedisManager $redis;
public function __construct()
{
if (empty($this->bucket) || empty($this->hashFunction))
{
throw new AppException("需要定义bucket和hashFunction");
}
$this->hashFunctionObj = new HashFunction();
$this->redis = app()->get('redis');
}
/**
* 添加到集合中
*/
public function add(string $bucket,string $string) : void
{
/**
* @var \Redis $pipe
*/
$pipe = $this->redis->multi();
foreach ($this->hashFunction as $function)
{
$hash = $this->handlerHashNum(
call_user_func([$this->hashFunctionObj,$function->value],$string)
);
$this->redis->setBit($bucket ?? $this->bucket, $hash, 1);
}
$pipe->exec();
}
public function handlerHashNum($num) : int
{
$limit = 11;
if($num > $limit * 100000000)
{
$num = substr($num,0,9);
}
return intval($num);
}
/**
* 查询是否存在, 如果曾经写入过,必定回true,如果没写入过,有一定几率会误判为存在
*/
public function exists(string $bucket,string $string) : bool
{
/**
* @var \Redis $pipe
*/
$pipe = $this->redis->multi();
$len = strlen($string);
foreach ($this->hashFunction as $function)
{
$hash = $this->handlerHashNum(
call_user_func([$this->hashFunctionObj,$function->value],$string)
);
$pipe = $pipe->getBit($bucket ?? $this->bucket, $hash);
}
$res = $pipe->exec();
foreach ($res as $bit)
{
if ($bit == 0)
{
return false;
}
}
return true;
}
}
b、integer 版 - redis
namespace App\Components\BloomFilter;
use App\Components\BloomFilter\Core\BloomFilterCore;
use App\Exceptions\AppException;
use Illuminate\Redis\RedisManager;
/**
* 布隆过滤 integer 性能版
*/
class BloomFilterIntegerBase extends BloomFilterCore
{
/**
* bucket 名称
*
* @var string
*/
protected string $bucket = 'integer_base';
protected RedisManager $redis;
public function __construct()
{
if (empty($this->bucket))
{
throw new AppException("需要定义bucket和hashFunction");
}
$this->redis = app()->get('redis');
}
public function add(string $bucket, string $string): void
{
$this->redis->setBit($bucket ?? $this->bucket, intval($string), 1);
}
public function exists(string $bucket, string $string): bool
{
return $this->redis->getBit($bucket ?? $this->bucket, intval($string)) != 0;
}
}
关于使用
//布隆过滤 - 通用版 - 测试
$key = 'mall_shop_2';
\App\Components\BloomFilter\BloomFilter::add('test1',$key);
$result2 = \App\Components\BloomFilter\BloomFilter::exists('test1',$key);
dd([
// '$result1' => $result1,
'$result2' => $result2,
]);
//布隆过滤 - integer版 - 测试
$key = '1111';
\App\Components\BloomFilter\BloomFilterInteger::add('test1',$key);
$result2 = \App\Components\BloomFilter\BloomFilterInteger::exists('test1',$key);
dd([
// '$result1' => $result1,
'$result2' => $result2,
]);