利用MySQL实现域名黑名单过滤10W记录1ms匹配

先说一下这个需求,需要判断某个域名是否在黑名单中。黑名单中的域名一旦设置,其下任何级的域名都认为是在黑名单中。

如黑名单中加入a.com,则a.com、www.a.com、1.2.3.a.com等都算进入黑名单。

如黑名单中加入com,则所有com域名都算进入黑名单。

下面出现的域名如带*都为违法域名

一开始我使用了like去做查询,如我要做判断的域名是www.a.com。SQL语句如下:

select count(*) from tb_domain_blacklist where '.www.a.com' like concat('%.', domain)

数据量少的情况下速度很快,我测试了一下10W黑名单时,大概在200毫秒左右。

虽然黑名单一般不会有这么多,但是我有研究的兴趣,所以就继续思考。

接下来想到的办法,10W黑名单时,耗时1-3毫秒左右。

我在表中加入了hash字段,设为索引,这里直接用了md5对域名进行处理。

id	hash	domain
1	07ad577047b305c8a3a2e732eb271404	gp-***.com
2	8b32be4fdce459e135c207eb00426e8a	milail-***.com
3	fd0857db5a002dff0d4f4643bd2a061d	ni2.org

然后在php中在查询前对域名进行处理。因为域名是用.来分割一段一段的,所以我把每一段的md5都计算出来,最终进行in查询。由于域名的一段一段不会太多,所以并不需要担心性能问题。

比如域名是www.baidu.com,那么我将com、baidu.com、www.baidu.com分别md5,最终代入in查询条件中。

代码如下:

/**
 * 检查域名是否存在于黑名单。true存在,false不存在
 * @param string $domain
 * @return boolean
 */
public function check($domain)
{
    $domainSplitList = array_reverse(explode('.', $domain));
    $tmpDomain = '';
    $hashes = [];
    foreach($domainSplitList as $domainItem)
    {
        if(isset($tmpDomain[0]))
        {
            $tmpDomain = $domainItem . '.' . $tmpDomain;
        }
        else
        {
            $tmpDomain = $domainItem;
        }
        $hashes[] = md5($tmpDomain);
    }
    return $this->where(array('hash'=>array('in', $hashes)))->count() > 0;
}

最终结果:

[SQL]select count(*) from tb_domain_blacklist where `hash` in ('a', 'b', 'c', '6c0470f373f41d4d54097676f0d96333');
受影响的行: 0
时间: 0.002s

[SQL]
select count(*) from tb_domain_blacklist where '.futu***.com' like concat('%.', domain)

受影响的行: 0
时间: 0.193s


  • 标签
  • 发表评论
当前用户:
  • 评论列表
  • memory 2018-02-11 16:17:54

    如果直接将domain加索引了?

    • 宇润 2018-02-12 08:53:42

      如果要用like实现字段值是a.com,则a.com、www.a.com、1.2.3.a.com等都算进入黑名单。那就要这么写like '%a.com',这样是不会走索引的

  • 小黑屋 2017-10-29 19:30:35

    这样的话,所有.com结尾的域名全匹配到了?

    • 宇润 2017-11-09 08:53:07

      如果设置com,那就所有com域名都匹配