Oracle+Python適合 Oracle DBA 使用的 Python


傳統上,當需要為操作系統編寫一些腳本時,人們常常會選用 Bash 或 Perl 腳本工具。這些工具易於使用,因而它們幾乎變得無處不在,滲透到了包括 Oracle Database 在內的其他軟件中,Oracle Database 在很大程度上依賴它們執行各種管理任務。

但是最近,這種趨勢有所轉變,轉向有利於 Python 這類較新的編程工具。Python 可為我們提供直觀的開發以及各種靈活的數據結構和庫。所有的現代 Unix 和 Linux 系統都附帶了 Python;例如,Oracle Linux 6.1 附帶了 Python 2.6.6。

本教程將介紹對數據庫管理員尤為有用的一些 Python 特性,無論這些管理員要實現一次性代碼段還是完全可重用的程序,這些特性對他們來說都十分有用。在本部分中,我們將探究如何與操作系統和遠程資源交互,然后了解各種壓縮和文件系統遍歷模塊。

出於本教程的目的,我們將在 Oracle Linux 6.1 和 Python 2.6.6 環境中使用 Oracle Database 11g 快捷版 (XE)。


與文件系統交互

Python 用來與操作系統交互的核心庫是 os 模塊,您可以通過此模塊處理系統進程、識別平台、處理操作系統管道以及使用環境變量 — 以 100 多個函數和變量的形式。

檢測當前平台如同訪問 os 模塊中的預定義字符串那樣容易。以下示例展示了 Oracle Linux 6.1 上的結果,並且還顯示了此操作系統的默認路徑分隔符。

>>> import os
>>> os.name
‘posix’
>>> os.sep
‘/’

通過 os.environ 可以訪問所有 Oracle 環境變量的列表。以下示例利用一個內聯生成器表達式:

>>> import os
>>> oracle_vars = dict((a,b) for a,b in os.environ.items() if a.find('ORACLE')>=0)
>>> from pprint import pprint
>>> pprint(oracle_vars)
{'ORACLE_HOME': '/u01/app/oracle/product/11.2.0/xe', 'ORACLE_SID':'XE'}

這相當於

SELECT key, value 
FROM os.environ 
WHERE key LIKE ‘%ORACLE%’ 

(若用 SQL 編寫)。

當我們進一步探索時,我們開始查看文件系統並了解所處位置。下表列出了最常用的文件系統訪問函數及其描述。 

函數

作用

os.getcwd()

獲取操作系統中的當前工作目錄

os.chdir(path)

將目錄更改為給定 path

os.chroot(path)

將當前 Python 進程的根路徑更改為 path

os.chown(pathuidgid)

與 chmod Linux 命令相同(uid 和 gid 是編號)

os.listdir(path)

列出給定 path 下的文件和目錄

os.mkdir(pathmode)

在給定 path 下創建目錄,並將八進制權限設置為 mode(默認為 0777)

os.remove(path)

刪除 path 下的一個文件

os.rmdir(path)

刪除 path 下的目錄

os.rename(pathnewpath)

將 path 重命名為 newpath

os.stat(path)

使用 OS stat() 調用顯示 path 的屬性

os.walk(pathtopdown,onerrorfollowlinks)

針對 path 下的文件系統樹返回生成器返回字節組(路徑、目錄、文件)

 

掌握了支持文件系統瀏覽的基本功能之后,我們來了解一下如何使用 Python 快速查看舊的跟蹤文件和“未輪轉”日志的列表並顯示它們使用了多少空間。清單 1 中的程序需要兩個參數:Oracle 日志路徑(DIAGNOSTIC_DEST 指向的目錄)和文件被視為過時的天數。此示例基於 os.walk


清單 1. walk.py:Oracle 診斷目錄下的舊的日志和跟蹤文件

import datetime
import os
import sys
import time
from pprint import pprint

def readable(size):
  si=('B','KB','MB','GB','TB', 'PB', 'EB', 'ZB', 'YB')
  div = [n for n, m in enumerate(si) if pow(1024, n+1)>size][0]
  return "%.1f%s"%(size/float(pow(1024, div)), si[div])

total = {"log":0, "trace":0}
for path, dirs, files in os.walk(sys.argv[1]):
  for f in files:
    filepath = path+os.sep+f
    if os.stat(filepath).st_mtime>time.time()-(3600*24*int(sys.argv[2])):
      size = readable(os.path.getsize(filepath))
      age = datetime.datetime.fromtimestamp(os.stat(filepath).st_mtime)
      if f in ("log.xml", "alert.log", "listener.log"):
        filetype = "log"
      elif f.endswith("trc") or f.endswith("trm"):
        filetype = "trace"
      else:
        filetype = None
      if filetype:
        total[filetype] += os.path.getsize(filepath)

for a, b in total.items():
  total[a] = readable(b)

pprint(total)

運行 walk.py 得到如下輸出:

$ python walk.py /home/oracle/app 10
{'log': '132.0MB', 'trace':'0.0B'}

在 os 命名空間中,另有一個名為 os.path 的模塊,用於解決路徑名稱操作。它包含適用於不同系統的平台敏感的實現,因此導入 os.path 將始終獲得正確的操作系統版本。

os.path 模塊中的常用函數包括:

  • basename(path),用於獲得給定路徑的葉名稱
  • dirname(path),用於獲得文件路徑的目錄部分;它由 split(path) 函數加以補充,后者返回包含隔開的目錄部分和文件部分的字節組
  • exists(path),用於查看路徑下是否存在文件,針對無法解析的符號鏈接返回 False
  • getsize(path),用於快速查看路徑下的字節數
  • isfile(path) 和 isdir(path),用於解析路徑類型


雖然目前為止我們已經了解了一些豐富的文件系統瀏覽功能,但我們也只是初涉皮毛,因為還有多個其他模塊。例如,filecmp 模塊既能夠比較文件又能夠比較目錄,tempfile 可以輕松地管理臨時文件,glob 解析符合 Unix 式模式的文件路徑(如在 ora_pmon_*.trc、log_*.xml 中,等等),非常有用的 shutil 模塊實現高級文件系統操作,如復制和刪除多個文件或整個文件樹。 

與進程通信


os 模塊並不僅限於文件管理。還可以用來與系統進程交互和生成系統進程,以及執行系統 kill 和 nice 調用。下表列出了最有用的進程管理函數。這些函數只對 Unix 和 Linux 平台有效,但在 Python 3.2 分支中正在進行一些工作以使這些函數可用於 Windows。 

函數

作用

os.abort()

向當前 Python 進程發送 SIGABRT

os.exec*(patharg1...argN,environ)

exec* 函數系列,用於以 path 指定的進程取代當前進程,可選擇提供命令行參數和環境變量

os.kill(pidsignal)

向給定 pid 發送 signal

os.nice(value)

更改當前進程的 nice 值

os.popen(commandmode,buffersize)

對給定 command 打開一個未命名管道,有效地實現與進程的進一步交互;mode 表示管道打開處理屬性(默認為“r”,表示讀取)

os.spawn*(modepath,environ)

在一個新的進程中運行 path 下的程序(這些函數現在已被 subprocess 模塊棄用)

os.system(command)

此函數通過操作系統 system() 調用(該調用可用於 Unix 和 Windows)運行由 command 定義的新進程



雖然其中許多函數可能在較舊的 Python 版本中派上用場,但從版本 2.4 開始,專門創建了一個專用的 subprocess 模塊來管理進程。這個新模塊最初在 2003 年提交到 Python 增強建議索引 (PEP),現在成為與系統進程通信的首選方法。

Subprocess 以簡單、可用並且相當通用的接口取代 os.popen、os.spawn* 和 os.system 函數。清單 2 顯示了 ps.py 程序的代碼,此程序執行 ps aux 命令並將結果移到 Python 字典中。這里使用了一個管道來作為 stdout 的目標以捕獲所有信息,並阻止輸出到屏幕。

清單 2. ps.py:將系統進程映射移到 Python 字典中
import re
import subprocess

args = ['ps', 'aux']
ps = subprocess.Popen(args, stdout=subprocess.PIPE)
processes = ps.stdout.readlines()
header = re.split('\s+', processes.pop(0))[:-1]
header.remove('COMMAND')

PS = {}
for process in processes:
  columns = re.split('\s+', process)
  if columns[0]!='oracle':
       continue
  PS[int(columns[1])] = {}
  for position, column in enumerate(columns[:9]):
       PS[int(columns[1])][header[position].lower()] = column
       PS[int(columns[1])]['command'] = ' '.join(columns[10:])

from pprint import pprint
pprint(PS)


輸出如下:

...
 25892: {'%cpu': '0.0',
       '%mem': '3.9',
       'command': 'xe_w000_XE ',
       'pid': '25892',
       'rss': '23672',
       'start': '16:02',
       'stat': 'Ss',
       'tty': '?',
       'user': 'oracle',
       'vsz': '457240'},
 26142: {'%cpu': '2.0',
       '%mem': '0.9',
       'command': 'python proc.py ',
       'pid': '26142',
       'rss': '5732',
       'start': '16:36',
       'stat': 'S+',
       'tty': 'pts/2',
       'user': 'oracle',
       'vsz': '160776'},
 26143: {'%cpu': '0.0',
       '%mem': '0.1',
       'command': 'ps aux ',
       'pid': '26143',
       'rss': '1100',
       'start': '16:36',
       'stat': 'R+',
       'tty': 'pts/2',
       'user': 'oracle',
       'vsz':'108044'}}

popen 函數接受多個關鍵字參數,如 stdin/stdout/stderr 描述符、用於設置進程工作目錄的 cwd,或者設置子進程環境變量的 env。要查看命令的狀態,只需查看 returncode 屬性。進程標識符在 pid 屬性下提供。

針對已創建進程的方法包括用於查看進程是否仍在運行的 poll()、用於在程序完成時進行恢復的 wait()、用於發送特定信號的 send_signal(),以及分別用於發送 SIGTERM 或 SIGKILL 信號的 terminate() 或 kill()。最后,要與生成的子進程完全交互,我們使用 communicate() 函數發送 stdin 輸入。

為了對此進行說明,我們來創建一個基於遺留的 SYSDBA 連接而發展的簡單 SQL*Plus 包裝器。

清單 3. sp.py:通過 Python 與 SQL*Plus 進程通信
import os
from subprocess import Popen, PIPE

sqlplus = Popen(["sqlplus", "-S", "/", "as", "sysdba"], stdout=PIPE, stdin=PIPE)
sqlplus.stdin.write("select sysdate from dual;"+os.linesep)
sqlplus.stdin.write("select count(*) from all_objects;"+os.linesep)
out, err = sqlplus.communicate()
print out

This return output similar to:

SYSDATE
--------------
02-DEC-11

COUNT(*)
--------------
76147

報告服務

涉及走出數據庫的一項最令人頭疼的任務是發送警報或推送從數據倉庫提取的經常性報告。好消息是,Python 不僅成功實現了一個全球流行的郵件列表系統 — Mailman,而且還提供一個豐富的電子郵件處理庫,此庫支持 MIME、附件、消息編碼以及與電子郵件處理有關的各個方面。email 模塊將協議本身內容與表示層相分離以便僅專注於構建郵件消息,而交付工作通過 smtplib 模塊處理。

email.message 中的 Message 類代表用於處理電子郵件的核心類。email.mime 命名空間中的各個處理程序用於處理不同的附件類型。但在此示例中,我們將使用最通用的一個處理程序:email.mime.base 中的 MIMEBase。不過我們這方面略施小計,利用了電子表格軟件將以表格格式打開 HTML 文件(如果它們具有 .xls 擴展名)的事實。我們還將利用 tempfile 模塊的幫助。

Oracle Linux 並未預先安裝 cx_Oracle 模塊,因此您將需要從 cx-oracle.sourceforge.net 獲得此模塊。此外,為了能夠導入 cx_Oracle 並使用網絡配置文件,在啟動 Python 解釋器之前需要設置 ORACLE_HOME 和 LD_LIBRARY_PATH。 

[root@xe ~]# rpm -ivh cx_Oracle-5.1-11g-py26-1.x86_64.rpm
Preparing...########################################### [100%]
1:cx_Oracle ########################################### [100%]
[root@xe ~]# 
[root@xe ~]# su - oracle
[oracle@xe ~]$ export ORACLE_HOME=/u01/app/oracle/product/11.2.0/xe
[oracle@xe ~]$ export LD_LIBRARY_PATH=$ORACLE_HOME/lib


請參見清單 4 了解完整程序,此程序連接到 Oracle Database 11g XE,提取員工數據,並將此數據打包為電子表格附件以便發送到電子郵件組。

清單 4. report.py:電子郵件報告服務

import cx_Oracle
import datetime
import smtplib
import tempfile
from email.message import Message
from email.encoders import encode_base64
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart

today = datetime.datetime.now()

msg = MIMEMultipart()
msg['From'] = 'Reports Service <reports@company.intranet>'
msg['To'] = 'receipients@company.intranet'
msg['Subject'] = 'Monthly employee report %d/%d ' % (today.month, today.year)

db = cx_Oracle.connect('hr', 'hrpwd', 'localhost/xe')
cursor = db.cursor()
cursor.execute("select * from employees order by 1")
report = tempfile.NamedTemporaryFile()
report.write("<table>")
for row in cursor:
  report.write("<tr>")
  for field in row:
    report.write("<td>%s</td>" % field)
  report.write("</tr>")
report.write("</table>")
report.flush()
cursor.close()
db.close()

attachment = MIMEBase('application', 'vnd.ms-excel')
report.file.seek(0)
attachment.set_payload(report.file.read())
encode_base64(attachment)
attachment.add_header('Content-Disposition', 'attachment;filename=emp_report_%d_%d.xls' % (today.month, today.year))
msg.attach(attachment)

emailserver = smtplib.SMTP("localhost")
emailserver.sendmail(msg['From'], msg['To'], msg.as_string())
emailserver.quit()

如果我們更進一步采用此示例,可以使用 Python 圖形處理庫 (PIL) 獲取統計圖,附加存儲在數據庫中的 BLOB 的縮略圖,或者借助 ReportLab 生成 PDF 報告以便發送到相關組。email 模塊的功能非常強大,足以應對每種可能的情形。

總結

Python 豐富的跨平台模塊庫確實完善了 DBA 的技術組合,DBA 使用這些技術能夠監視整個數據庫體系、加快開發速度,同時保持極低的維護開銷。Python 使用廣泛,每個現代 Linux 平台都附帶了這一工具,這可以進一步提高其采用率,並且隨着時間的推移,有助於它成為可滿足所有數據庫管理需求的新的理想語言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM