【问题标题】:Importing MaxMind's GeoLite2 to MySQL将 MaxMind 的 GeoLite2 导入 MySQL
【发布时间】:2015-08-14 23:16:29
【问题描述】:

MaxMind 的GeoLite2 是一个很棒的数据库,如果您想将IP 地址映射到国家/地区,它非常有用。

为了有效地做到这一点,我想用这样的方案将它导入 MySQL 数据库:

我记得很久以前为 CSV 数据库编写了一个导入脚本,但是您今天可以下载的 CSV 格式很难理解,至少对我来说是这样:

network,geoname_id,registered_country_geoname_id,represented_country_geoname_id,is_anonymous_proxy,is_satellite_provider 1.0.0.0/24,2077456,2077456,,0,0 1.0.1.0/24,1814991,1814991,,0,0 1.0.2.0/23,1814991,1814991,,0,0 1.0.4.0/22,2077456,2077456,,0,0 1.0.8.0/21,1814991,1814991,,0,0 1.0.16.0/20,1861060,1861060,,0,0 1.0.32.0/19,1814991,1814991,,0,0 1.0.64.0/18,1861060,1861060,,0,0 1.0.128.0/17,1605651,1605651,,0,0

我真的被困在了这里的基础上。 将数据库从其 CSV 表示形式导入 MySQL 的最有效和最简单的方法是什么?

【问题讨论】:

    标签: mysql import geoip geolite2


    【解决方案1】:
    network,geoname_id,registered_country_geoname_id,represented_country_geoname_id,is_anonymous_proxy,is_satellite_provider
    1.0.0.0/24,2077456,2077456,,0,0
    1.0.1.0/24,1814991,1814991,,0,0
    1.0.2.0/23,1814991,1814991,,0,0
    1.0.4.0/22,2077456,2077456,,0,0
    
    create table thing1
    (   network varchar(20) not null,
        geoname_id varchar(20) not null,
        registered_country_geoname_id varchar(20) not null,
        represented_country_geoname_id varchar(20) not null,
        is_anonymous_proxy varchar(20) not null,
        is_satellite_provider varchar(20) not null
    );
    
    LOAD DATA INFILE 'c:\\dev\\ipaddr.txt' 
    INTO TABLE thing1
    FIELDS TERMINATED BY ',' 
    LINES TERMINATED BY '\n'
    IGNORE 1 LINES
    (@v1,@v2,@v3,@v4,@v5,@v6)
    set network=ifnull(@v1,''),
    geoname_id=ifnull(@v2,''),
    registered_country_geoname_id=ifnull(@v3,''),
    represented_country_geoname_id=ifnull(@v4,''),
    is_anonymous_proxy=ifnull(@v5,''),
    is_satellite_provider=ifnull(@v6,'');
    

    以上对我来说很好。

    下面开始编辑

    对于以下内容,尝试在 stijn-de-witt 在 cmets 中提出的观点之后改进此答案。

    但是请注意,cmets 中引用的博客在进入整数的更新语句中存在错误。所以暂时直到我解决这个问题,我发现了一个 varchar 修改,如下所示。

    Edit1(更多内容,请参阅下面的 cmets 答案):

    Alter the table to get a "from ip to ip range"
    
    alter table thing1 add column from_ip varchar(20), add column to_ip varchar(20);
    -- note that those two are nullable at the moment. You can always change that later
    

    为刚刚添加的 varchar 更新表

    update thing1
    set from_ip=  INET_NTOA(INET_ATON( SUBSTRING_INDEX(network, '/', 1)) 
       & 0xffffffff ^ ((0x1 << ( 32 - SUBSTRING_INDEX(network, '/', -1))  ) -1 )),
    to_ip=   INET_NTOA(INET_ATON( SUBSTRING_INDEX(network, '/', 1)) 
       | ((0x100000000 >> SUBSTRING_INDEX(network, '/', -1) ) -1 ))
    select * from thing1;
    

    (对于上述更新声明,感谢 Bernd Buffen 在此answer

    以上更新声明的结果:

    mysql> select network,from_ip,to_ip from thing1;
    +------------+---------+-----------+
    | network    | from_ip | to_ip     |
    +------------+---------+-----------+
    | 1.0.1.0/24 | 1.0.1.0 | 1.0.1.255 |
    | 1.0.2.0/23 | 1.0.2.0 | 1.0.3.255 |
    | 1.0.4.0/22 | 1.0.4.0 | 1.0.7.255 |
    +------------+---------+-----------+
    

    从这里,查看 MySQL 手册页 Miscellaneous Functions 以获取 INET_ATON(expr)

    Edit2(再次感谢stijn-de-witt):

    alter table thing1 add column uint_from_ip int unsigned, add column uint_to_ip int unsigned;
    
    UPDATE thing1 SET uint_from_ip = inet_aton(SUBSTRING(network, 1, LOCATE('/', network) - 1)), 
    uint_to_ip = (inet_aton(SUBSTRING(network, 1, LOCATE('/', network) - 1)) + (pow(2, (32-CONVERT(SUBSTRING(network, LOCATE('/', network) + 1), UNSIGNED INT)))-1));
    

    结果:

    select network,from_ip,to_ip,uint_from_ip,uint_to_ip from thing1;
    +------------+---------+-----------+--------------+------------+
    | network    | from_ip | to_ip     | uint_from_ip | uint_to_ip |
    +------------+---------+-----------+--------------+------------+
    | 1.0.1.0/24 | 1.0.1.0 | 1.0.1.255 |     16777472 |   16777727 |
    | 1.0.2.0/23 | 1.0.2.0 | 1.0.3.255 |     16777728 |   16778239 |
    | 1.0.4.0/22 | 1.0.4.0 | 1.0.7.255 |     16778240 |   16779263 |
    +------------+---------+-----------+--------------+------------+
    

    (在提到一些修复后,以上归功于 S0BEIT blog

    【讨论】:

    • 到目前为止还不错,但它根本不符合我的原理图。另外,IP从哪里来?如果我们将 IP 地址编码为 32 位整数,那么我们应该有两个名为“from”和“to”的 int 列,对吧?
    • 只是想分享一些字节,而不是绕着它鞠躬。请原谅:>
    • 我对 IPv4 了解的不多。所以对我来说,了解如何将“223.252.161.128/25”之类的表示形式转换为 IP from-to 范围会很有帮助。
    • s0beit's blog 描述了如何使用简单的 SQL 查询将 network 字段转换为 range_beginrange_end 字段。我还没有使用它,但它看起来合法:UPDATE `GeoLite2-Country-Blocks-IPv4` SET range_begin = inet_aton(SUBSTRING(network, 1, LOCATE('/', network) - 1)), range_end = (inet_aton(SUBSTRING(network, 1, LOCATE('/', network) - 1)) + (pow(2, (32-CONVERT(SUBSTRING(network, LOCATE('/', network) + 1), INTEGER)))-1))
    • @StijndeWitt 感谢分享。这肯定会更好,我会尝试利用这些信息来改进这一点。
    【解决方案2】:

    我建议使用MaxMind's GeoIP2 CSV Converter 将其设置为您想要的格式。有一个-include-integer-range 选项将提供我认为您正在寻找的整数列。二进制文件可用于Windows, Linux (amd64), and OS X

    【讨论】:

      【解决方案3】:

      用一个简单的 SQL 脚本似乎确实无法做到这一点,所以我用 C# 编写了一个。而且由于导入这么大的 MySQL 数据库并不是那么简单,所以我在脚本本身中直接实现了INSERT INTO

      需要一个类似于问题草图中的表格结构才能工作。

      using MySql.Data.MySqlClient;
      using System;
      using System.Collections.Generic;
      using System.IO;
      using System.Linq;
      
      namespace GeoIPConvert
      {
          public static class Program
          {
              public static void Main(string[] args)
              {
                  // https://dev.maxmind.com/geoip/geoip2/geolite2/
      
                  List<Country> countries = File.ReadAllLines("Countries.csv")
                      .Select(line => line.Split(','))
                      .Where(line => line[4] != "" && line[5] != "")
                      .Select((line, index) => new Country
                      {
                          ID = Convert.ToInt32(line[0]),
                          DatabaseID = index + 1,
                          Flag = line[4].ToLower(),
                          Name = line[5].Replace("\"", "")
                      })
                      .ToList();
      
                  List<IPRange> ipRanges = File.ReadAllLines("GeoIP.csv")
                      .Select(line => line.Split(','))
                      .Where(line => line[2] != "")
                      .Select(line => new IPRange
                      {
                          Country = countries.First(country => country.ID == Convert.ToInt32(line[2])),
                          From = ConvertCidrToRange(line[0]).Item1,
                          To = ConvertCidrToRange(line[0]).Item2,
                      })
                      .ToList();
      
                  //string sql =
                  //  "INSERT INTO geoip_countries(Flag, Name) VALUES\r\n" +
                  //  string.Join(",\r\n", countries.Select(country => "(\"" + country.Flag + "\", \"" + country.Name + "\")").ToArray()) + "\r\n" +
                  //  "INSERT INTO geoip_ipranges(CountryID, `From`, `To`) VALUES\r\n" +
                  //  string.Join(",\r\n", ipRanges.Select(iprange => "(\"" + iprange.Country.DatabaseID + "\", \"" + iprange.From + "\", \"" + iprange.To + "\")").ToArray());
      
                  //File.WriteAllText("Import.sql", sql);
      
                  using (MySqlConnection sql = new MySqlConnection("Server=localhost;Database=test_db;Uid=root;"))
                  {
                      sql.Open();
      
                      foreach (Country country in countries)
                      {
                          new MySqlCommand("INSERT INTO geoip_countries(Flag, Name) VALUES(\"" + country.Flag + "\", \"" + country.Name + "\")", sql).ExecuteNonQuery();
                      }
                      foreach (IPRange ipRange in ipRanges)
                      {
                          new MySqlCommand("INSERT INTO geoip_ipranges(CountryID, `From`, `To`) VALUES(\"" + ipRange.Country.DatabaseID + "\", \"" + ipRange.From + "\", \"" + ipRange.To + "\")", sql).ExecuteNonQuery();
                          Console.WriteLine(ipRange.To);
                      }
      
                      sql.Close();
                  }
              }
      
              private static Tuple<uint, uint> ConvertCidrToRange(string cidr)
              {
                  string[] parts = cidr.Split('.', '/');
                  uint ipnum = Convert.ToUInt32(parts[0]) << 24 | Convert.ToUInt32(parts[1]) << 16 | Convert.ToUInt32(parts[2]) << 8 | Convert.ToUInt32(parts[3]);
                  uint mask = uint.MaxValue << (32 - Convert.ToInt32(parts[4]));
                  return Tuple.Create(ipnum & mask, ipnum | (mask ^ uint.MaxValue));
              }
          }
      
          public class Country
          {
              public int ID { get; set; }
              public int DatabaseID { get; set; }
              public string Flag { get; set; }
              public string Name { get; set; }
          }
      
          public class IPRange
          {
              public Country Country { get; set; }
              public uint From { get; set; }
              public uint To { get; set; }
          }
      }
      

      【讨论】:

        【解决方案4】:

        您可以使用我的工具从 GitHub https://github.com/mbto/maxmind-geoip2-csv2sql-converter 将 MaxMind GeoLite2 国家/城市 CSV 转换为 MySQL/PostgreSQL/Microsoft SQL Server 2019

        您可以阅读examples,或按照以下步骤操作:

        1. https://support.maxmind.com/account-faq/license-keys/how-do-i-generate-a-license-key/ 获取 MaxMind API 的免费许可证密钥(如果您没有)
        2. adoptopenjdk.netgithub.com/raphw/raphw.github.iooracle.com/java 安装Java 11(如果未安装)
        3. releases(.zip 或 .tar)下载工具
        4. 解压到你的目录
        5. 使用您的个人资料名称复制/粘贴 .ini 模板 bin/GeoLite2-Country-CSV.mysql.default.ini,例如 bin/GeoLite2-Country-CSV.mysql.Your Project Name.ini 或使用默认值。
        6. 使用记事本打开 .ini 模板并更改 [windows_loader][unix_loader] 部分(设置 MySQL 主机:端口、用户和密码)。
        7. 对于 unix:执行 chmod +x maxmind-geoip2-csv2sql-converter
        8. 运行转换:maxmind-geoip2-csv2sql-converter.bat -c "GeoLite2-Country-CSV.mysql.Your Project Name.ini" -k Your_License_Key -i 4,6
        9. 转换后,脚本bin/converted/loader.batbin/converted/loader.sh将可用。
        10. 对于 unix:执行 chmod +x loader.sh
        11. 执行loader.batloader.sh 以导入架构。

        完成

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-10-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-29
          • 1970-01-01
          相关资源
          最近更新 更多