视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
地理位置geo处理之mysql函数的详细介绍(附代码)
2020-11-27 14:11:16 责编:小采
文档
 本篇文章给大家带来的内容是关于地理位置geo处理之mysql函数的详细介绍(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

目前越来越多的业务都会基于LBS,附近的人,外卖位置,附近商家等等,现就讨论离我最近这一业务场景的解决方案。

目前已知解决方案有:

mysql 自定义函数计算mysql geo索引mongodb geo索引postgresql PostGis索引redis geoElasticSearch

本文测试下mysql 函数运算的性能

准备工作

创建数据表

CREATE TABLE `driver` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `lng` float DEFAULT NULL,
 `lat` float DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建测试数据

在创建数据之前先了解下基本的地理知识:

  • 全球经纬度的取值范围为: 纬度-90~90,经度-180~180
  • 中国的经纬度范围大约为: 纬度3.86~53.55,经度73.66~135.05
  • 北京行政中心的纬度为39.92,经度为116.46
  • 越北面的地方纬度数值越大,越东面的地方经度数值越大
  • 度分转换: 将度分单位数据转换为度单位数据,公式:度=度+分/60
  • 分秒转换: 将度分秒单位数据转换为度单位数据,公式:度 = 度 + 分 / 60 + 秒 / 60 / 60
  • 在纬度相等的情况下:

  • 经度每隔0.00001度,距离相差约1米
  • 在经度相等的情况下:

  • 纬度每隔0.00001度,距离相差约1.1米
  • mysql函数计算

    DELIMITER //
    CREATE DEFINER=`root`@`localhost` FUNCTION `getDistance`(
     `lng1` float(10,7) 
     ,
     `lat1` float(10,7)
     ,
     `lng2` float(10,7) 
     ,
     `lat2` float(10,7)
    
    ) RETURNS double
     COMMENT '计算2坐标点距离'
    BEGIN
     declare d double;
     declare radius int;
     set radius = 6371000; #假设地球为正球形,直径为6371000米
     set d = (2*ATAN2(SQRT(SIN((lat1-lat2)*PI()/180/2) 
     *SIN((lat1-lat2)*PI()/180/2)+ 
     COS(lat2*PI()/180)*COS(lat1*PI()/180) 
     *SIN((lng1-lng2)*PI()/180/2) 
     *SIN((lng1-lng2)*PI()/180/2)), 
     SQRT(1-SIN((lat1-lat2)*PI()/180/2) 
     *SIN((lat1-lat2)*PI()/180/2) 
     +COS(lat2*PI()/180)*COS(lat1*PI()/180) 
     *SIN((lng1-lng2)*PI()/180/2) 
     *SIN((lng1-lng2)*PI()/180/2))))*radius;
     return d;
    END//
    DELIMITER ;

    创建数据python脚本

    # coding=utf-8
    from orator import DatabaseManager, Model
    import logging
    import random
    import threading
    
    """ 中国的经纬度范围 纬度3.86~53.55,经度73.66~135.05。大概0.00001度差距1米 """
    
    # 创建 日志 对象
    logger = logging.getLogger()
    handler = logging.StreamHandler()
    formatter = logging.Formatter(
     '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    logger.setLevel(logging.DEBUG)
    
    # Connect to the database
    
    config = {
     'mysql': {
     'driver': 'mysql',
     'host': 'localhost',
     'database': 'dbtest',
     'user': 'root',
     'password': '',
     'prefix': ''
     }
    }
    
    db = DatabaseManager(config)
    Model.set_connection_resolver(db)
    
    
    class Driver(Model):
     __table__ = 'driver'
     __timestamps__ = False
     pass
    
    
    def ins_driver(thread_name,nums):
     logger.info('开启线程%s' % thread_name)
     for _ in range(nums):
     lng = '%.5f' % random.uniform(73.66, 135.05)
     lat = '%.5f' % random.uniform(3.86, 53.55)
    
     driver = Driver()
     driver.lng = lng
     driver.lat = lat
     driver.save()
    
    thread_nums = 10
    for i in range(thread_nums):
     t = threading.Thread(target=ins_driver, args=(i, 400000))
     t.start()

    以上脚本创建10个线程,10个线程插入4万条数据。耗费150.18s执行完,总共插入40万条数据

    测试

  • 测试环境
  • 系统:mac os

    内存:16G

    cpu: intel core i5

    硬盘: 500g 固态硬盘

    测试下查找距离(134.38753,18.56734)这个坐标点最近的10个司机

    select *,`getDistance`(134.38753,18.56734,`lng`,`lat`) as dis from driver ORDER BY dis limit 10
  • 耗时:18.0s
  • explain:全表扫描
  • 我测试了从1万到10万间隔1万和从10万到90万每间隔10万测试的结果变化

    结论

  • 此方案在数据量达到3万条查询耗时就会超过1秒
  • 大约每增加1万条就会增加0.4秒的耗时
  • 下载本文
    显示全文
    专题