問題:
接到需求,告知項目的oracle連接次數過多,對系統造成太過大的負擔,要求減少oracle數據庫的連接次數
分析:
仔細分析代碼以后,發現產生問題的原因,在於之前要求提升oracle監控的監控速度時,將oracle監控的腳本代碼,拆分成了多個子進程。導致每次循環服務器都會產生子進程次數的數據庫連接,產生了過多的不必要連接
解決方案:
討論分析過后,決定更改代碼的架構,用DBUtils的連接池功能+多線程(http://www.cnblogs.com/fnng/p/3670789.html)的組合,替代現有的 多進程+子進程對數據庫的單次連接
DBUtils:
DBUtils 是一套允許線程化 Python 程序可以安全和有效的訪問數據庫的模塊。
下載並安裝:
$ wget https://pypi.python.org/packages/65/65/89afee016aca7fbb5c1642e6ef3864d80af808dc5efa7367b328093eece9/DBUtils-1.1.tar.gz tar -zxf DBUtils-1.1.tar.gz cd DBUtils-1.1 python setup.py install
使用:
參考:http://blog.163.com/power_mr/blog/static/138744007201391823253744/
導入:import DBUtils.PersistentDB
- dbapi :數據庫接口
- mincached :啟動時開啟的空連接數量
- maxcached :連接池最大可用連接數量
- maxshared :連接池最大可共享連接數量
- maxconnections :最大允許連接數量
- blocking :達到最大數量時是否阻塞
- maxusage :單個連接最大復用次數
#不用連接池的MySQL連接方法 import MySQLdb conn= MySQLdb.connect(host='localhost',user='root',passwd='pwd',db='myDB',port=3306) cur=conn.cursor() SQL="select * from table1" r=cur.execute(SQL) r=cur.fetchall() cur.close() conn.close()
#用連接池后的連接方法 import MySQLdb from DBUtils.PooledDB import PooledDB pool = PooledDB(MySQLdb,5,host='localhost',user='root',passwd='pwd',db='myDB',port=3306) #5為連接池里的最少連接數 conn = pool.connection() #以后每次需要數據庫連接就是用connection()函數獲取連接就好了 cur=conn.cursor() SQL="select * from table1" r=cur.execute(SQL) r=cur.fetchall() cur.close() conn.close()
自用:
生成mysql池
# get mysql PooledDB logger.info("get MySQL connect.") host = get_config('monitor_server','host') port = get_config('monitor_server','port') user = get_config('monitor_server','user') passwd = get_config('monitor_server','passwd') dbname = get_config('monitor_server','dbname') try: # mincached 最少的空閑連接數,如果空閑連接數小於這個數,pool會創建一個新的連接 mysql_pool = PooledDB(MySQLdb,mincached=20,blocking=True,host=host,user=user,passwd=passwd,port=int(port),db=dbname,connect_timeout=5,charset='utf8') glob.set_value('mysql_conn',mysql_pool) except Exception,e: print "start mysql(pooledDB) error:" + str(e)
生成oracle池:
#get oracle servers list logger.info("get Oracle connect.") servers=func.mysql_query("select id,host,port,dsn,username,password,tags from db_servers_oracle where is_delete=0 and monitor=1;") if servers: for row in servers: server_id=row[0] host=row[1] port=row[2] dsn=row[3] username=row[4] password=row[5] tags=row[6] ora_dsn = host + ":" + port + "/" + dsn try: oracle_pool = PooledDB(cx_Oracle,mincached=20,blocking=True,user=username,password=password,dsn=ora_dsn) conn_name = "server_"+str(server_id)+"_pool" glob.set_value(conn_name,oracle_pool) except Exception, e: logger_msg="check oracle server connect %s : %s" %(ora_dsn,str(e).strip('\n')) logger.warning(logger_msg)
問題:
改動過程中,碰到了比較難以解決的“疑難雜症”:腳本在執行過程中,會出現異常導致無限執行的死循環中斷現象,同時出現中斷時,不產生異常報錯,導致bug的解決比較困難
為了找出異常的部分,對代碼進行了更詳細的測試:
- 搭建代碼框架,進行框架測試
- 向框架中填充基本內容,進行基本內容測試
- 對代碼的各個部分,進行實驗性測試:
連接池 + 單index線程循環 + 服務器循環外join + 執行oracle操作 -> 異常退出 單連接 + 單index線程循環 + 服務器循環外join + 執行oracle操作 -> 異常退出 單連接 + 單index線程循環 + 服務器循環外join + 不執行oracle操作 -> 正常 連接池 + 單index線程循環 + 服務器循環外join + 不執行oracle操作 -> 正常 連接池 + 單index線程循環 + 服務器循環外join + 執行單條oracle操作 -> 異常
確認了問題產生於oracle數據庫
確認了問題所在后,經過多次試驗,發現在使用多線程連接數據庫的過程中,打印出了兩次異常:
KPEDBG_HDL_POP_FCPTRKPEDBG_HDL_POP_FCPTRKPEDBG_HDL_POP_FCPTRORA-24550: signal received: [si_signo=11] [si_errno=0] [si_code=1] [si_int=16] [si_ptr=0x10] [si_addr=(nil)]
百度后修改oracle客戶端的配置文件(sqlnet.ora)
#路徑:/usr/lib/oracle/12.2/client64/network/admin/sqlnet.ora DIAG_ADR_ENABLED=OFF DIAG_SIGHANDLER_ENABLED=FALSE DIAG_DDE_ENABLED=FALSE
同時,改動oracle數據庫的使用方式:
從之前獲取連接以后,使用連接作為oralce監控方法的傳輸參數,
改為,傳遞oracle連接池的池連接(oralce_pool)作為參數,在方法內,生成新的oracle連接
1. 搭建代碼框架,進行框架測試
