Nginx Map:could not build optimal map_hash, you should increase either map_hash_max_size: 2048

错误信息

Nginx Map:could not build optimal map_hash, 
you should increase either map_hash_max_size: 2048 or map_hash_bucket_size: 64; ignoring map_hash_bucket_size

Nginx Map Module介绍

Module ngx_http_map_module

map 指令介绍:

map 指令是由 ngx_http_map_module 模块提供的,默认情况下安装 nginx 都会安装该模块。

map 的主要作用是创建自定义变量,通过使用 nginx 的内置变量,去匹配某些特定规则,如果匹配成功则设置某个值给自定义变量。 而这个自定义变量又可以作于他用。

直接看个例子理解起来比较清晰:

  • 场景: 匹配请求 url 的参数,如果参数是 debug 则设置 $foo = 1 ,默认设置 $foo = 0
map $args $foo {
    default 0;
    debug   1;
}

解释:

$args 是nginx内置变量,就是获取的请求 url 的参数。 如果 $args 匹配到 debug 那么 $foo 的值会被设为 1 ,如果 $args 一个都匹配不到 $foo 就是default 定义的值,在这里就是 0

map 涉及的性能问题

       大家可能会有一个问题,map 既然只能用在 http 段,这是全局的啊。 这个设置会让访问所有虚拟主机的请求都要匹配并设置一遍变量的值,然而事实并非如此,对于没有用到相关变量的请求来说,并不会执行 map 操作。 就没有性能上的损失。

匹配优先级问题

如果匹配到多个特定的变量,如掩码和正则同时匹配,那么会按照下面的顺序进行选择:

  1. 没有掩码的字符串
  2. 最长的带前缀的字符串,例如: “*.example.com”
  3. 最长的带后缀的字符串,例如:“mail.*”
  4. 按顺序第一个先匹配的正则表达式 (在配置文件中体现的顺序)
  5. 默认值

map_hash_bucket_size

  • 语法: map_hash_bucket_size size;
  • 默认值: map_hash_bucket_size 32|64|128;
  • 配置段: http
  • 指定一个映射表中的变量在哈希表中的最大值,这个值取决于处理器的缓存。

map_hash_max_size

  • 语法: map_hash_max_size size;
  • 默认值: map_hash_max_size 2048;
  • 配置段: http
  • 设置映射表对应的哈希表的最大值。

hashes

Setting up hashes

To quickly process static sets of data such as server names, map directive’s values, MIME types, names of request header strings, nginx uses hash tables. During the start and each re-configuration nginx selects the minimum possible sizes of hash tables such that the bucket size that stores keys with identical hash values does not exceed the configured parameter (hash bucket size). The size of a table is expressed in buckets. The adjustment is continued until the table size exceeds the hash max size parameter. Most hashes have the corresponding directives that allow changing these parameters, for example, for the server names hash they are server_names_hash_max_size and server_names_hash_bucket_size.

The hash bucket size parameter is aligned to the size that is a multiple of the processor’s cache line size. This speeds up key search in a hash on modern processors by reducing the number of memory accesses. If hash bucket size is equal to one processor’s cache line size then the number of memory accesses during the key search will be two in the worst case — first to compute the bucket address, and second during the key search inside the bucket. Therefore, if nginx emits the message requesting to increase either hash max size or hash bucket size then the first parameter should first be increased.

map 语法

map $var1 $var2 {...}

map 指令的三个参数:

1、default : 指定源变量匹配不到任何表达式时将使用的默认值。当没有设置 default,将会用一个空的字符串作为默认的结果。

2、hostnames : 允许用前缀或者后缀掩码指定域名作为源变量值。这个参数必须写在值映射列表的最前面。

3、include : 包含一个或多个含有映射值的文件。

  • 在 Nginx 配置文件中的作用段: http{} ,注意 map 不能写在 server{} 否则会报错

map 的 $var1 为源变量,通常可以是 nginx 的内置变量,$var2 是自定义变量。 $var2 的值取决于 $var1 在对应表达式的匹配情况。 如果一个都匹配不到则 $var2 就是 default 对应的值。

  • 一个正则表达式如果以 “~” 开头,表示这个正则表达式对大小写敏感。以 “~*”开头,表示这个正则表达式对大小写不敏感。
map $http_user_agent $agent {
    default "";
    ~curl curl;
    ~*apachebench" ab;
}
  • 正则表达式里可以包含命名捕获和位置捕获,这些变量可以跟结果变量一起被其它指令使用。
map $uri $value {
    /abc                       /index.php;
    ~^/teacher/(?<suffix>.*)$  /boy/;
    ~/fz(/.*)                  /index.php?fz=1;                           
}
  • 注意一:不能在map块里面引用命名捕获或位置捕获变量。如~^/qupeicom/(.*) /peiyin/$1; 这样会报错nginx: [emerg] unknown variable
  • 注意二:如果源变量值包含特殊字符如‘~’,则要以‘\’来转义。
map $http_referer $value {
    Mozilla    111;
    \~Mozilla  222;
}
  • 源变量匹配表达式对应的结果值可以是一个字符串也可以是另外一个变量。
map $http_referer $value {
    Mozilla    'chrom';
    \~safity    $http_user_agent;

实例(一)

使用 map 来实现允许多个域名跨域访问的问题

如果是允许单域名跨域访问直接配置就行了,如下:

# 这些配置可以写在 http{} 或者 server{} 都是支持的。
add_header Access-Control-Allow-Origin "http://www.tutu.com";
add_header Access-Control-Allow-Methods "POST, GET, PUT, OPTIONS, DELETE";
add_header Access-Control-Max-Age "3600";
add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept;

#!/bin/bash
# 上面的配置只允许 http://www.tutu.com 跨域访问,如果要支持所有域名都可以跨域调用该站。  把上面一行改成这样,不过不推荐这样做,因为不安全
add_header Access-Control-Allow-Origin "*";

如果不想允许所有,但是又需要允许多个域名,那么就需要用到 map

map $http_origin $corsHost {
    default 0;
    "~http://www.haibakeji.com" http://www.haibakeji.com;
    "~http://m.haibakeji.com" http://m.haibakeji.com;
    "~http://wap.haibakeji.com" http://wap.haibakeji.com;
}
server
{
    listen 80;
    server_name www.haibakeji.com;
    root /nginx;
    location /
    {
        add_header Access-Control-Allow-Origin $corsHost;
    }
}

实例(二)

  • 使用源变量(通常是 nginx 内置变量)匹配一些规则,创建自定义变量,然后在页面输出. 这通常在调试的时候非常有用

    http {
    map $uri $match {
        ~^/hello/(.*) http://www.hello.com/;
    }
    server {
        listen       8080;
        server_name  test.hello.com;
    
        location /hello {
                default_type text/plain;
                echo uri: $uri;
                echo match: $match;
                echo capture: $1;
                echo new: $match$1;
        }

分析:

       保持 map 指令的键值对(包含在代码片段文件中)尽可能小的另一个原因是 Nginx 使用哈希表来快速处理静态数据集。

      当处理大量数据时(在我们的例子中有数千个 id 映射)Nginx 会在启动时抛出这个警告:

Nginx Map:could not build optimal map_hash, 
you should increase either map_hash_max_size: 2048 or map_hash_bucket_size: 64; ignoring map_hash_bucket_size

正如 Nginx 文档中所述:

在启动和每次重新配置期间,nginx 选择哈希表的最小可能大小,以便存储具有相同哈希值的键的桶大小不超过配置的参数(哈希桶大小)。表的大小以桶表示。调整一直持续到表大小超过散列最大大小参数为止。

       这意味着我们必须在映射指令之前设置map_hash_max_size和值。map_hash_bucket_size问题在于,除了我们的数据大小之外,处理器还会影响最佳值,这意味着我们必须调整这些值并测试它们,因为实际上不可能事先估计它们。

一般建议是使这两个值尽可能小

  • 如果只要nginx 它警告,就先增加map_hash_max_size。如果数字超过某个大数字(例如 32769),只要它警告,就将 map_hash_bucket_size增加到平台上默认值的倍数。如果只要它不再警告,就减少map_hash_max_size。现在您已经为您的一组键设置了最佳设置(每组键可能需要不同的设置)。
  • 更大的 map_hash_max_size意味着消耗更多的内存。
  • 更大的 map_hash_bucket_size意味着更多的 CPU 周期(对于每个键查找)以及更多从主内存到缓存的传输。
  • max_size 与键数没有直接关系,如果键数翻倍,您可能需要将 max_size 增加 10 倍甚至更多以避免冲突。如果您无法避免它们,则必须增加 bucket_size。
  • 据说 bucket_size 增加到 2 的下一个幂,从源代码我判断它应该足以使其成为默认值的倍数,这应该保持传输到缓存的最佳状态。
  • bucket_size 的大小取决于密钥的长度。如果平均密钥大小为 32 字节(具有哈希数组开销),将 bucket_size 增加到 512 字节意味着它可以容纳 16 个具有冲突哈希键的密钥。这不是您想要的,如果发生碰撞,它会线性搜索。您希望碰撞尽可能少。
  • 如果您的 max_size 小于 10000 且 bucket_size 较小,您可能会遇到较长的加载时间,因为 nginx 会尝试在循环中找到最佳哈希大小。
  • 如果您的 max_size 大于 10000,那么在它警告之前将“仅”执行 1000 次循环。

在我们有 7200 个项目的 id 映射集的情况下,我们首先增加 map_size,然后测试我们的 Ngnix 重新配置:

sudo nginx -t

      我们不断增加它,直到不再有 Nginx 警告,但这导致了一个非常大的数字,所以我们将 bucket_size 加倍到 128,然后再次开始使用 max_size,将它从原来的 2048增加 4 倍。

这测试后产生以下映射指令:

map_hash_max_size 8192;
map_hash_bucket_size 128;

map $request_uri $old_id {
  ~^/media/([0-9]+) $1;
}

map $old_id $new_id {
  include /etc/nginx/snippets/rewritemap.conf;
}

   

参考:

Module ngx_http_map_module

URL mapping with Nginx · Extending PeerTube

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值