Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > вставка в Оракл большого количество строк


Автор: cube 17.4.2015, 14:54
Вставляю в таблицу базы данных Оракл большое количество строк, процесс занимает не много времени. Текстовый файл csv размером 400 мб, и 800 000 записей, загружается в бд, за 3 минуты.

Но хотелось бы еще быстрее. Код выполняется на серверной jre, на сервере с огромным числом процессоров и оперативной памяти, бд тоже крутится на этом же сервере. Таблица индексов, ключей не имеет. Перед загрузкой данных происходит очищение всей таблицы.

К сожалению весь код привести не могу, но для понимания, процессы разделил на потоки. Один поток читает файл использует ArrayBlockingQueue (10000, false). 2 других производят вставку в таблицу. Каждый процесс коннекшен берет из пула, использует PreparedStatement и batch. Для чистоты эксперимента (чтобы понять какой процесс происходит дольше остальных), заменил метод put() на add() и увеличил размер очереди до 100 000, не проходит и 15 секунд как очередь переполняется и вываливается эксепшен.

Добавил еще 10 потоков для вставки в бд, всеравно вываливается эксепшен о переполнении очереди, заменил обратно add() на put() замерил время, с 10 потоками на вставку ушло еще на 2 минуты больше. Я понимаю что в этот системе узкое место вставка, но как ускорить ее? Свойства таблицы менять NOLOGGING задаю, никаких измененений в скорости не наблюдаю.

Код

public class Consumer extends Thread
{
    final static Logger log = StartPoint.getLogger();
    private int addedRows = 0;
    private int batchSize = 0;
    private BlockingQueue<Object[]> q;
    private String preparedSQL;
    
    private Connection conn;
    private PreparedStatement stmt;
    
    public Consumer(BlockingQueue<Object[]> q, Connection conn, String preparedSQL, int batchSize)
    {
        super();
        this.q = q;
        this.conn = conn;
        this.preparedSQL = preparedSQL;
        this.batchSize = batchSize;
    }
    
    @Override
    public void run() 
    {
        try {
            stmt = conn.prepareStatement(preparedSQL);
            Object[] data = new Object[0];
            int i = 0;
            while ((data = q.take()).length>0)
            {
                for ( i=0; i<data.length; i++)
                    stmt.setObject(i+1, data[i]);
                stmt.addBatch();
                if( ++addedRows % batchSize == 0)
                {
                    stmt.executeBatch();
                    stmt.clearBatch();      
                }
            }
            stmt.executeBatch();
            conn.commit();
            conn.close();
        } catch (Exception ex) {
            log.error("Can,t insert data to DB!", ex);
        }
    }
    public int getAddedRows(){return addedRows;}
}




Автор: LSD 17.4.2015, 18:58
Скорее всего тут узкое место не Java а Oracle. На стороне Java достаточно батчинга. Запусти один поток и посмотри нагрузку CPU, если нагрузка на ядро со стороны Java не превышает 50%, то больше потоков не нужно. Если превышает, то можно попробовать добавить.
Со стороны Oracle надо смотреть, что у него там тупит, но скорее всего это IO.

Автор: cube 19.4.2015, 19:16
да там загрузка 3,7% это смешно. IO имеете ввиду что? Вообще это база данных, к тому же очень шустрая. 

Автор: LSD 20.4.2015, 12:41
Цитата(cube @  19.4.2015,  20:16 Найти цитируемый пост)
IO имеете ввиду что?

Запись на диск.


Цитата(cube @  19.4.2015,  20:16 Найти цитируемый пост)
Вообще это база данных, к тому же очень шустрая.

Иди к DBA пусть смотрит, что там тупит.

Автор: AntonSaburov 20.4.2015, 17:34
1. Надо проверить как очищается таблица - если прямолинейно delete from table - то это может быть проблемой при большом числе записей.
2. Остальное LSD уже посоветовал - я тоже склоняюсь к тому, что здесь узкое место именно сам Оракл.

Автор: cube 21.4.2015, 16:07
таблица перед загрузкой очищается sql запросом TRUNCATE TABLE, это достаточно быстрая процедура. Тем более что без commit'a.

есть идеи создавать временные таблицы, закидать быстренько туда, а уже потом процедурой на сервере зааппендить к реальной, как думаете в целом ускорит процесс? Возможности потерять данные велики?

Автор: LSD 21.4.2015, 18:33
Цитата(cube @  21.4.2015,  17:07 Найти цитируемый пост)
есть идеи создавать временные таблицы, закидать быстренько туда, а уже потом процедурой на сервере зааппендить к реальной, как думаете в целом ускорит процесс?

Что значит "зааппендить"? Ты же truncate делаешь, она должна быть пустая.

Автор: cube 22.4.2015, 09:46
имел ввиду hint /*+ APPEND */
Код


........ 
TRUNCATE TABLE MY_TABLE; 
........
INSERT /*+ APPEND */ INTO MY_TABLE.....    SELECT..... FROM MY_TMP_TABLE;
........
COMMIT;
........


сорри в pl/sql не силен

Автор: LSD 22.4.2015, 12:31
Цитата(cube @  22.4.2015,  10:46 Найти цитируемый пост)
hint /*+ APPEND */

Стоит как минимум попробовать его использовать.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)