MYSQL 查询优化

本文主要目的就是对数据库的优化查询,但查询过程中碰到了一个不明缘由的问题,希望有大神能够帮忙解释一波。

另:不支持图片外链,我没找到什么好方法来上传图片,如果有需要看图片的,可以移步到我的博客中去看,本篇文章的博客地址在本文末。


目的

从 csv 文件中随机选取 100 条 Ip 地址,并在表中查询这 100条 ip 的所在地。

步骤

  1. 读取样本,组装列表
  2. 表建立索引
  3. 优化 sql 语法
  4. 联合查询

读取文件

读取文件和上一篇文章所讲的一样,利用 with 语法避免文件句柄关闭操作,给 open 函数增加 encoding 参数。

然后将读取的数据进行拆分和整理:

    with open('./ipdata.csv', 'r', encoding='utf-8') as f:
        lines = f.readlines()

    nl_p_list = []
    for l in lines:
        ls = l.strip().split(',', 4)
        c1, c2, c3, c4, c5 = ls[0], ip2int(ls[1]), ip2int(ls[2]), ls[3], ls[4]
        nl = [c1, c2, c3, c4, c5]
        nl_p_list.append(nl)

表建立索引

使用 navicat 连接表,进入 「设计表」 模块,为 startip 和 endip 建立索引。

[图片上传失败...(image-c12667-1518151013339)]

优化 sql 语法

如果使用复杂语法的 sql 语句,是无法利用索引大幅提升查询效率的,比如通过 between and 查询:

SELECT * FROM ipdata WHERE IpNum BETWEEN startip AND endip

因为 ip 是连续的,所以只要这个 Ip 地址大于或等于某一个号段的起始地址,那么依照降序排序第一的 ip 段的所在地,就是被查询的 ip 地址的所在地。

SELECT * FROM ipdata WHERE IpNum >= startip ORDER BY startip DESC LIMIT 1

经过对比发现,查询速度果然是有质的飞跃。但也出现了问题。问题详情我会在文末写明。

联合查询

利用索引的 sql 语句果然是如有神助,在我的运行环境里, between and 查询语句,查询 100 条记录需要 32s+ ,但在优化后能提升到 0.7s 左右。

但是在使用联合查询后,还能提升将近 2 倍的速率。

联合查询的 sql 语法:

SELECT t1.* FROM (SELECT * FROM ipdata WHERE 1780997668 >=  startip ORDER BY startip DESC  LIMIT 1) t1
UNION ALL
SELECT t2.* FROM (SELECT * FROM ipdata WHERE 3425736747 >=  startip ORDER BY startip DESC  LIMIT 1) t2;

下一步,就是加工 sql 联合语句了。联合查询的 sql 语句,不宜过长,比较适合的长度就是 100 条。

sql_str = 'SELECT {0}.* FROM (SELECT * FROM ipdata WHERE %s >=  startip ORDER BY startip DESC  LIMIT 1) {0}'
for i in range(len(ip_list)):
    sql_list.append(sql_str.format('t' + str(i)) % ip_list[i])
sql = ' union all '.join(sql_list)

使用 format 函数填充别名, %s 填充 IP 地址 。
最后执行 sql 语句:

cursor.execute(sql)

效果也是有的,将查询的速度提升到了 0.3s 左右。

至此, sql 的数据表查询优化部分那就结束了。

遇到的灵异现象

如图所示,我注释了 t1, t2, t3, t4 之间的代码。

[图片上传失败...(image-f064cf-1518151013339)]

时间段 含义 运行速度
t0 ~ t1 联合优化 sql 语句查询 0.0318
t1 ~ t2 优化 sql 语句查询 9.5367
t3 ~ t2 between and 语句查询 1.1920
t4 ~ t3 联合 between and 语句查询 0.0

事实上,除了 t0 ~ t1 之间的时间是大致在 0.03s ~ 0.07s 波动,剩余的三个时间段的代码已经被我注释,理论上应该是不耗费时间的,但会随机在剩余的三个时间段 打印出 9s 左右的耗时。希望有大神能够帮助一波。


源码地址:
『NiuCodeLesson/insertSQL/』:https://github.com/wengfe/NiuCodeLesson/tree/master/insertSQL

博客地址:
『wengfe.win』:http://www.wengfe.win/2018/02/07/2018-02-08/#more

你可能感兴趣的:(MYSQL 查询优化)