本地访问 oracle 和线上访问 oracle 速度差异原因排查及解决

# 测试脚本:

if __name__ == '__main__':
    from sqlalchemy.engine import create_engine
    from sqlalchemy.pool import NullPool
    import pandas as pd
    import time
    import datetime
    db_url = 'xxx'
    engine = create_engine(db_url, poolclass=NullPool)
    sql = "xxx"
    start = datetime.datetime.now()
    df = pd.read_sql_query(sql, engine)
    end = datetime.datetime.now()
    print('start: %s' % start.strftime('%Y-%m-%d %H:%M:%S'))
    print('end: %s' % end.strftime('%Y-%m-%d %H:%M:%S'))
    print('End!')

线上执行状况测试

(venv) [root@zsj2sat302 test]# python db-test.py 
start: 2021-11-29 02:08:07
end: 2021-11-29 02:08:07
End!

# 故障描述

线上生产环境访问某个指定页面速度大致为 2~3 秒
本地开发环境访问该指定页面速度为 20~30 秒
其他页面同样遇到这种情况,严重影响开发效率

# 原因排查

# 猜测一: 物理机房位置原因,该 PC 机网段的实际网络链路效率问题

抓包测试,线上抓包,本地抓包。我们本地开发环境代码和线上环境代码是一样的,但是地理位置不一样,本地开发环境即个人 PC 在 china 境内,而产线机器在美国境内。所以首先猜测是因为跨太平洋远距离数据传输问题导致延迟。 其他 team 也感觉和产线比有一些慢。

# 猜测二: 中国 GFW 问题

因为 GFW 审查,公司内部网络走 HK 代理,猜测可能和代理有关,或者其他相关数据审查有关。有可能,但不好验证。

# 猜测三: Python ORM 框架版本问题

将 python 本地虚拟环境涉及到 db 的第三方 module 版本都调整和产线环境一样后发现速度还是很慢,排除!

cx-Oracle            8.1.0
Flask-SQLAlchemy     2.4.4
pandas               0.25.3
SQLAlchemy           1.3.22

# 猜测四: OS 相关环境问题

可能是 windows 环境或者本地 oracle instant client 的配置或版本问题

# 本地 vmware 安装 centos8.4,测试 centos8.4 上相同 python 脚本执行速度

[root@localhost ~]$ cat /etc/redhat-release 
[root@localhost ~]# cd /opt/
[root@localhost opt]# whereis python
[root@localhost opt]# virtualenv -p /usr/bin/python3.6 venv
[root@localhost opt]# mkdir test
[root@localhost opt]# cd test/
[root@localhost test]# touch db-test.py
[root@localhost test]# vi db-test.py  (赋值测试 code 内容)
[root@localhost venv]# source bin/activate
(venv) [root@localhost venv]# pip list
(venv) [root@localhost venv]# pip install SQLAlchemy==1.3.22
(venv) [root@localhost venv]# pip install Flask-SQLAlchemy==2.4.4
(venv) [root@localhost venv]# pip install cx-Oracle==8.1.0
(venv) [root@localhost test]# pip install pandas==0.25.3
(venv) [root@localhost test]# python -V
(venv) [root@localhost test]# python db-test.py   (Cannot locate a 64-bit Oracle Client library 错误,下面装 oracle client)
(venv) [root@localhost opt]# mkdir oracle
(venv) [root@localhost opt]# cd oracle/
(venv) [root@localhost oracle]# wget https://download.oracle.com/otn_software/linux/instantclient/185000/instantclient-basic-linux.x64-18.5.0.0.0dbru.zip
(venv) [root@localhost oracle]# unzip instantclient-basic-linux.x64-18.5.0.0.0dbru.zip
(venv) [root@localhost oracle]# sudo yum install libaio
(venv) [root@localhost oracle]# sudo yum install libnsl
(venv) [root@localhost oracle]# vi ~/.bash_profile   (写入 export LD_LIBRARY_PATH=/opt/oracle/instantclient_18_5:$LD_LIBRARY_PATH)
(venv) [root@localhost oracle]# source ~/.bash_profile 
(venv) [root@localhost test]# python db-test.py 
start: 2021-11-28 19:02:50
end: 2021-11-28 19:03:00
End!
(venv) [root@localhost test]# python db-test.py 
start: 2021-11-28 19:12:51
end: 2021-11-28 19:12:59
End!

# Jones 从 mac os 帮忙测试的结果(WFH -> vpn)

start: 2021-11-29 03:14:20
end: 2021-11-29 03:14:28
End!

# Icon 从 windows 虚拟的 ubuntu 帮忙测试的结果

start: 2021-11-29 10:23:43
end: 2021-11-29 10:23:53
End!
start: 2021-11-29 10:41:27  (换另外一种连接oracle的方式后同样慢)
end: 2021-11-29 10:41:32

排除!

# 解决办法

# 尝试使用 iptables + NAT 配置跳板机

以三台机器 ip 为例

  1. A: 192.168.0.1 (类比本地 ip)
  2. B: 192.168.0.2 (类比跳板机 ip)
  3. C: 192.168.0.3 (类比 oracle 服务器 ip)
[root@localhost ~]# vi /etc/sysctl.conf
# Controls IP packet forwarding
# net.ipv4.ip_forward = 1    (设为 1 开启 Forword)
[root@localhost ~]# /sbin/sysctl -p   (生效)
[root@localhost ~]# service iptables status
[root@localhost ~]# systemctl start iptables.service
[root@localhost ~]# service iptables status
[root@localhost ~]# iptables -L -n
[root@localhost ~]# iptables -X
[root@localhost ~]# iptables -F
[root@localhost ~]# iptables -Z
[root@localhost ~]# iptables -t nat -A PREROUTING -p tcp -m tcp --dport 11521 -j DNAT --to-destination 192.168.0.3:1701
[root@localhost ~]# iptables -t nat -A POSTROUTING -p tcp -m tcp --dport 1521 -j SNAT --to-source 192.168.0.2
[root@localhost ~]# iptables-save
[root@localhost ~]# iptables -A PREROUTING -p tcp -m tcp -d 192.168.0.2 -j DNAT --to-destination 192.168.0.3:1701
[root@localhost ~]# iptables -A POSTROUTING -p tcp -m tcp -d 192.168.0.1 -j SNAT --to-source 192.168.0.2
[root@localhost ~]# iptables-save
[root@localhost ~]# iptables -L -n
[root@localhost ~]# systemctl restart iptables.service

参考

  1. https://cloud.tencent.com/developer/article/1115750
  2. https://blog.51cto.com/quliren/2047191

# 使用 socat 转发数据包

[root@localhost ~]# yum install -y socat
[root@localhost ~]# #下面是监听 192.168.1.252 网卡的 15672 端口,并将请求转发至 172.17.0.15 的 15672 端口。
[root@localhost ~]#  socat  -d -d -lf /var/log/socat.log TCP4-LISTEN:15672,bind=192.168.1.252,reuseaddr,fork TCP4:172.17.0.15:15672
  1. -d -d 前面两个连续的 - d -d 代表调试信息的输出级别。
  2. -lf /var/log/socat.log 指定输出信息的文件保存位置。
  3. TCP4-LISTEN:15672 在本地建立一个 TCP IPv4 协议的监听端口,也就是转发端口。这里是 15672,请根据实际情况改成你自己需要转发的端口。
  4. bind 指定监听绑定的 IP 地址,不绑定的话将监听服务器上可用的全部 IP。
  5. reuseaddr 绑定一个本地端口。
  6. fork TCP4:172.17.0.15:15672 指的是要转发到的服务器 IP 和端口,这里是 172.17.0.15 的 15672 端口。
if __name__ == '__main__':
    from sqlalchemy.engine import create_engine
    from sqlalchemy.pool import NullPool
    import pandas as pd
    import time
    import datetime
    db_url = 'oracle://username:password@192.168.1.252:15672/?service_name=xxx'
    engine = create_engine(db_url, poolclass=NullPool)
    sql = "xxx"
    start = datetime.datetime.now()
    df = pd.read_sql_query(sql, engine)
    end = datetime.datetime.now()
    print('start: %s' % start.strftime('%Y-%m-%d %H:%M:%S'))
    print('end: %s' % end.strftime('%Y-%m-%d %H:%M:%S'))
    print('End!')
start: 2021-11-29 05:50:28
end: 2021-11-29 05:50:35
End!

还是慢
效果不行!

参考:

  1. https://blog.chaos.run/dreams/nat-vps-port-forwarding/
  2. https://www.hi-linux.com/posts/61543.html

# VM 环境内调试

这个是可行的,不过 sftp 代码开启自动同步会有一些耗时,并且每次远程连接服务器上的虚拟环境时也会有一些耗时,不过这些耗时相对较短,如果 db 查询实在是太慢的话可以暂时使用这种方法。否则,除非给网络 team 提 ticket 反应这个问题,让他们 tracking 一下链路,否则真的不好处理。

pycharm 配置:

  1. Tools -> Deployment -> Configuration
  2. Tools -> Deployment -> Automatic Uploads(Always)
  3. File -> Settings -> SSH Configurations
  4. File -> Settings -> Python Interpreter -> Add -> SSH Interpreter

参考:

  1. https://zhuanlan.zhihu.com/p/36843200