Как эффективно извлечь большой набор данных из Oracle в файл? - PullRequest
1 голос
/ 15 мая 2019

У меня есть сервер оракула, с которого мне нужно извлечь данные, используя python в файлы. Эти файлы используются нижестоящими системами в качестве входных данных.

Несколько технических деталей: Oracle и Python работают на разных серверах. База данных размещается на клиенте, а все сценарии выполняются на сервере AWS RHEL EC2. Детали экземпляра EC2 выделены на этом снимке экрана. screenshot.

Мой подход Для этого я выбираю Python-библиотеку Python для подключения к удаленному клиенту Oracle и извлечения данных с помощью SQL-запроса. Ниже приведен фрагмент кода, который извлекает данные на основе предоставленного SQL-запроса.

def fetch_data_to_file(self,curr,query,dataset):
    try:
        self.logger.info('Executing query: {}'.format(query))
        start = time.time()
        curr.execute(query)
        query_time = time.time()-start
        start = time.time()
        rowcount=0
        with open(dataset,'a+') as f:
            writer = csv.writer(f,delimiter='|')
            writer.writerow([i[0] for i in curr.description])
            self.logger.info('Writing file: {}'.format(dataset))
            while True:
                rows = curr.fetchmany(self.batch_limit)
                self.logger.info('Writing {} rows'.format(self.batch_limit))
                rowcount+=len(rows)
                if not rows:
                    break           

        self.timer.info('{} Query Execution Time: {} seconds'.format(dataset,query_time))
        self.timer.info('{} File Writing Time: {} seconds. at {} rows/second'.format(dataset,time.time()-start,int(rowcount / (time.time()-start))))

        self.logger.info("File written.")
    except Exception as e:
        self.error.info("Error in fetching data.Error: {}".format(e))
        raise SystemExit(1)

Набор данных, который я извлекаю, близок к несжатому размеру 8 ГБ (возвращено около 35 миллионов строк). И для загрузки файла на мой сервер EC2 требуется примерно 1,5 часа. Я протестировал несколько вариантов batch_limits и обнаружил, что 1Million - 2Million - оптимальный размер для пакетной загрузки данных, однако я не уверен, есть ли что-то еще, что я могу сделать более эффективно, чтобы выяснить, каким должен быть мой размер пакета.

Что еще я изучил Я искал в Интернете, чтобы выяснить способы записи больших наборов данных в файлы с использованием Python, и многие предлагали использовать Pandas. Я пытался понять это, но не смог. Кроме того, важно, чтобы я сохранил данные и их типы данных, извлекая их в файлы.

Мой вопрос здесь : могу ли я сделать что-нибудь лучше, чтобы сделать этот код более эффективным? Является ли Python лучшим языком для этого? (Обратите внимание, мне нужно иметь возможность автоматизировать работу на любом языке, который я выберу. На данный момент поиск лицензированных библиотек немного затруднителен из-за внутренних ценовых проблем в моей фирме).

Кроме того, не уверен, поможет ли это, но вот снимок использования моей памяти, пока код загружал данные (htop) enter image description here enter image description here

Ответы [ 2 ]

3 голосов
/ 15 мая 2019

Одной из возможностей было бы загрузить бесплатную утилиту «SQLcl», в основном SQL-Plus на основе Java, но она делает гораздо больше. Скачать здесь . Что вы можете сделать с SQLcl, это перенести его на клиентский компьютер и использовать для извлечения данных, а также позаботиться о разделителе для вас. Здесь я устанавливаю разделитель для символа трубы. Это может быть более эффективным, чем пытаться сделать это через Python, и вы все равно сможете написать сценарий и вызывать его из Python или откуда угодно.

$ sqlcl username/password@'<hostname>:<port>/ORACLE_SID'
> Set sqlformat delimited |
> Spool <some file to hold the data>
> Select * from <some table>;
> Spool off

И вышесказанное можно легко перенести в сценарий оболочки.

#!/bin/bash

sqlcl username/password@'<hostname>:<port>/ORACLE_SID' <<EOF
Set sqlformat delimited |
Spool <some file to hold the data>
Select * from <some table>;
Spool off
EOF

Пример

sqlcl> Select * from emp;
"EMPNO"|"ENAME"|"JOB"|"MGR"|"HIREDATE"|"SAL"|"COMM"|"DEPTNO"
7839|"KING"|"PRESIDENT"||17-NOV-81|5000||10
7698|"BLAKE"|"MANAGER"|7839|01-MAY-81|2850||30
7782|"CLARK"|"MANAGER"|7839|09-JUN-81|2450||10
7566|"JONES"|"MANAGER"|7839|02-APR-81|2975||20
7788|"SCOTT"|"ANALYST"|7566|09-DEC-82|3000||20
7902|"FORD"|"ANALYST"|7566|03-DEC-81|3000||20
7369|"SMITH"|"CLERK"|7902|17-DEC-80|800||20
7499|"ALLEN"|"SALESMAN"|7698|20-FEB-81|1600|300|30
7521|"WARD"|"SALESMAN"|7698|22-FEB-81|1250|500|30
7654|"MARTIN"|"SALESMAN"|7698|28-SEP-81|1250|1400|30
7844|"TURNER"|"SALESMAN"|7698|08-SEP-81|1500|0|30
7876|"ADAMS"|"CLERK"|7788|12-JAN-83|1100||20
7900|"JAMES"|"CLERK"|7698|03-DEC-81|950||30
7934|"MILLER"|"CLERK"|7782|23-JAN-82|1300||10
1 голос
/ 23 мая 2019

Очень быстрым решением для выгрузки данных в формате CSV является режим CSV в SQL * Plus 12.2.Если у вас его еще нет, вы можете бесплатно получить SQL * Plus из пакетов Instant Client .

Создать сценарий SQL ex.sql, например:

set feedback off
set arraysize 500
select * from locations;
exit

Вы можете / должны настроить ARRAYSIZE для оптимальной производительности.

Затем вызовите SQL * Plus с опцией -m 'csv on'.При этом используется новая подсистема быстрого ввода-вывода:

sqlplus -l -s -m 'csv on delim |' cj@'"localhost/orclpdb1"' @ex.sql

Обратите внимание, что опция -s сделает запрос пароля невидимым.

Вывод будет выглядеть следующим образом:

"LOCATION_ID"|"STREET_ADDRESS"|"POSTAL_CODE"|"CITY"|"STATE_PROVINCE"|"COUNTRY_ID"
1000|"1297 Via Cola di Rie"|"00989"|"Roma"||"IT"
. . .

Мое объявление о выпуске В блоге есть еще несколько деталей.

...