Oracle에서 10분 이내에 천만 개의 쿼리를 삽입하시겠습니까?
저는 파일로더 프로그램을 만들고 있습니다.
이 프로그램의 목적은 입력 파일을 가져와서 데이터를 변환한 다음 Oracle 데이터베이스에 업로드하는 것입니다.
제가 직면한 문제는 오라클에서 매우 큰 입력 데이터의 삽입을 최적화해야 한다는 것입니다.
데이터를 테이블에 업로드하고 있습니다. 예를 들어 ABC입니다.
저는 오라클에서 제공하는 OCI 라이브러리를 C++ 프로그램에서 사용하고 있습니다.구체적으로는 OCI Connection Pool을 사용하여 멀티로딩 및 Oracle에 로딩하고 있습니다. (http://docs.oracle.com/cd/B28359_01/appdev.111/b28395/oci09adv.htm )
표 ABC를 만드는 데 사용된 DDL 문은 다음과 같습니다.
CREATE TABLE ABC(
seq_no NUMBER NOT NULL,
ssm_id VARCHAR2(9) NOT NULL,
invocation_id VARCHAR2(100) NOT NULL,
analytic_id VARCHAR2(100) NOT NULL,
analytic_value NUMBER NOT NULL,
override VARCHAR2(1) DEFAULT 'N' NOT NULL,
update_source VARCHAR2(255) NOT NULL,
last_chg_user CHAR(10) DEFAULT USER NOT NULL,
last_chg_date TIMESTAMP(3) DEFAULT SYSTIMESTAMP NOT NULL
);
CREATE UNIQUE INDEX ABC_indx ON ABC(seq_no, ssm_id, invocation_id, analytic_id);
/
CREATE SEQUENCE ABC_seq;
/
CREATE OR REPLACE TRIGGER ABC_insert
BEFORE INSERT ON ABC
FOR EACH ROW
BEGIN
SELECT ABC_seq.nextval INTO :new.seq_no FROM DUAL;
END;
현재 데이터베이스에 데이터를 업로드하기 위해 다음 Query 패턴을 사용하고 있습니다.저는 OCI 연결 풀의 다양한 스레드를 통해 500개의 쿼리를 일괄적으로 데이터를 보냅니다.
사용된 SQL 삽입 쿼리 샘플 -
insert into ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source)
select 'c','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'a','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'b','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'c','g',NULL, 'test', 123 , 'N', 'asdf' from dual
위 쿼리에 대한 오라클의 실행 계획 -
-----------------------------------------------------------------------------
| Id | Operation | Name|Rows| Cost (%CPU) | Time |
-----------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 4 | 8 (0) | 00:00:01 |
| 1 | LOAD TABLE CONVENTIONAL | ABC | | | |
| 2 | UNION-ALL | | | | |
| 3 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
| 4 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
| 5 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
| 6 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
100만 줄 로드 프로그램의 실행 시간 -
Batch Size = 500
Number of threads - Execution Time -
10 4:19
20 1:58
30 1:17
40 1:34
45 2:06
50 1:21
60 1:24
70 1:41
80 1:43
90 2:17
100 2:06
Average Run Time = 1:57 (Roughly 2 minutes)
저는 이 시간을 더 최적화하고 줄여야 합니다.제가 직면한 문제는 업로드를 위해 1,000만 행을 넣을 때입니다.
1,000만 명의 평균 실행 시간은 = 21분으로 나타났습니다.
(제 목표는 이 시간을 10분 이하로 줄이는 것입니다.)
그래서 저는 다음과 같은 단계도 시도했습니다.
seq_no를 기준으로 테이블 ABC의 파티셔닝을 수행했습니까?30개의 파티션을 사용했습니다.100만 행으로 테스트 - 성능이 매우 떨어졌습니다.분할되지 않은 테이블보다 거의 4배 더 많습니다.
last_chg_date를 기준으로 테이블 ABC의 또 다른 분할.30개의 파티션을 사용했습니다.
2.a) 100만 행으로 테스트 - 분할되지 않은 테이블과 거의 동등한 성능을 보였습니다.차이가 거의 없었기 때문에 고려되지 않았습니다.
2.b) 다시 1,000만 행에서 동일한 테스트를 수행했습니다. 성능은 분할되지 않은 테이블과 거의 동일했습니다.눈에 띄는 차이는 없습니다.
다음은 파티셔닝을 달성하기 위해 사용된 DDL 명령입니다.
CREATE TABLESPACE ts1 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts2 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts3 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts4 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts5 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts6 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts7 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts8 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts9 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts10 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts11 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts12 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts13 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts14 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts15 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts16 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts17 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts18 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts19 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts20 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts21 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts22 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts23 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts24 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts25 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts26 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts27 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts28 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts29 DATAFILE AUTOEXTEND ON;
CREATE TABLESPACE ts30 DATAFILE AUTOEXTEND ON;
CREATE TABLE ABC(
seq_no NUMBER NOT NULL,
ssm_id VARCHAR2(9) NOT NULL,
invocation_id VARCHAR2(100) NOT NULL,
calc_id VARCHAR2(100) NULL,
analytic_id VARCHAR2(100) NOT NULL,
ANALYTIC_VALUE NUMBER NOT NULL,
override VARCHAR2(1) DEFAULT 'N' NOT NULL,
update_source VARCHAR2(255) NOT NULL,
last_chg_user CHAR(10) DEFAULT USER NOT NULL,
last_chg_date TIMESTAMP(3) DEFAULT SYSTIMESTAMP NOT NULL
)
PARTITION BY HASH(last_chg_date)
PARTITIONS 30
STORE IN (ts1, ts2, ts3, ts4, ts5, ts6, ts7, ts8, ts9, ts10, ts11, ts12, ts13,
ts14, ts15, ts16, ts17, ts18, ts19, ts20, ts21, ts22, ts23, ts24, ts25, ts26,
ts27, ts28, ts29, ts30);
OCI를 사용하여 스레드 함수(C++로 작성)에서 사용하고 있는 CODE -
void OracleLoader::bulkInsertThread(std::vector<std::string> const & statements)
{
try
{
INFO("ORACLE_LOADER_THREAD","Entered Thread = %1%", m_env);
string useOraUsr = "some_user";
string useOraPwd = "some_password";
int user_name_len = useOraUsr.length();
int passwd_name_len = useOraPwd.length();
text* username((text*)useOraUsr.c_str());
text* password((text*)useOraPwd.c_str());
if(! m_env)
{
CreateOraEnvAndConnect();
}
OCISvcCtx *m_svc = (OCISvcCtx *) 0;
OCIStmt *m_stm = (OCIStmt *)0;
checkerr(m_err,OCILogon2(m_env,
m_err,
&m_svc,
(CONST OraText *)username,
user_name_len,
(CONST OraText *)password,
passwd_name_len,
(CONST OraText *)poolName,
poolNameLen,
OCI_CPOOL));
OCIHandleAlloc(m_env, (dvoid **)&m_stm, OCI_HTYPE_STMT, (size_t)0, (dvoid **)0);
////////// Execution Queries in the format of - /////////////////
// insert into pm_own.sec_analytics (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value, override, update_source)
// select 'c','b',NULL, 'test', 123 , 'N', 'asdf' from dual
// union all select 'a','b',NULL, 'test', 123 , 'N', 'asdf' from dual
// union all select 'b','b',NULL, 'test', 123 , 'N', 'asdf' from dual
// union all select 'c','g',NULL, 'test', 123 , 'N', 'asdf' from dual
//////////////////////////////////////////////////////////////////
size_t startOffset = 0;
const int batch_size = PCSecAnalyticsContext::instance().getBatchCount();
while (startOffset < statements.size())
{
int remaining = (startOffset + batch_size < statements.size() ) ? batch_size : (statements.size() - startOffset );
// Break the query vector to meet the batch size
std::vector<std::string> items(statements.begin() + startOffset,
statements.begin() + startOffset + remaining);
//! Preparing the Query
std::string insert_query = "insert into ";
insert_query += Context::instance().getUpdateTable();
insert_query += " (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value, override, update_source)\n";
std::vector<std::string>::const_iterator i3 = items.begin();
insert_query += *i3 ;
for( i3 = items.begin() + 1; i3 != items.end(); ++i3)
insert_query += "union " + *i3 ;
// Preparing the Statement and Then Executing it in the next step
text *txtQuery((text *)(insert_query).c_str());
checkerr(m_err, OCIStmtPrepare (m_stm, m_err, txtQuery, strlen((char *)txtQuery), OCI_NTV_SYNTAX, OCI_DEFAULT));
checkerr(m_err, OCIStmtExecute (m_svc, m_stm, m_err, (ub4)1, (ub4)0, (OCISnapshot *)0, (OCISnapshot *)0, OCI_DEFAULT ));
startOffset += batch_size;
}
// Here is the commit statement. I am committing at the end of each thread.
checkerr(m_err, OCITransCommit(m_svc,m_err,(ub4)0));
checkerr(m_err, OCIHandleFree((dvoid *) m_stm, OCI_HTYPE_STMT));
checkerr(m_err, OCILogoff(m_svc, m_err));
INFO("ORACLE_LOADER_THREAD","Thread Complete. Leaving Thread.");
}
catch(AnException &ex)
{
ERROR("ORACLE_LOADER_THREAD", "Oracle query failed with : %1%", std::string(ex.what()));
throw AnException(string("Oracle query failed with : ") + ex.what());
}
}
게시물에 응답하는 동안 INSERT QUERY를 최적화하는 몇 가지 방법이 제안되었습니다.저는 다양한 INSERT 쿼리를 테스트하는 동안 다음과 같은 이유로 프로그램에서 QUERI를 선택하여 사용했습니다.나에게 제안된 SQL 쿼리 실행에 대해 - QUERI -
insert into ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source)
select 'c','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'a','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'b','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'c','g',NULL, 'test', 123 , 'N', 'asdf' from dual
쿼리 I에 대한 오라클의 실행 계획 -
--------------------------------------------------------------------------
| Id | Operation | Name| Rows | Cost (%CPU) | Time |
--------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 4 | 8 (0) | 00:00:01 |
| 1 | LOAD TABLE CONVENTIONAL | ABC | | | |
| 2 | UNION-ALL | | | | |
| 3 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
| 4 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
| 5 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
| 6 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
질문 II -
insert all
into ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source) values ('c','b',NULL, 'test', 123 , 'N', 'asdf')
into ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source) values ('c','e',NULL, 'test', 123 , 'N', 'asdf')
into ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source) values ('c','r',NULL, 'test', 123 , 'N', 'asdf')
into ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source) values ('c','t',NULL, 'test', 123 , 'N', 'asdf')
select 1 from dual
쿼리 II에 대한 오라클의 실행 계획 -
-----------------------------------------------------------------------------
| Id | Operation | Name| Rows | Cost (%CPU) | Time |
-----------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 1 | 2 (0) | 00:00:01 |
| 1 | MULTI-TABLE INSERT | | | | |
| 2 | FAST DUAL | | 1 | 2 (0) | 00:00:01 |
| 3 | INTO | ABC | | | |
| 4 | INTO | ABC | | | |
| 5 | INTO | ABC | | | |
| 6 | INTO | ABC | | | |
실험에 따르면 쿼리 I이 더 빠릅니다.
여기서 저는 Oracle SQL Developer와 C++ 프로그램(FILELOADER)에 의한 삽입 쿼리를 모두 테스트했습니다.
이에 대해 자세히 읽어보니 실행 계획에 표시된 비용은 쿼리가 자체 처리에 사용할 CPU의 수입니다.이는 Oracle이 첫 번째 쿼리를 처리하는 데 더 많은 CPU를 사용할 것이라는 것을 의미합니다. 따라서 Oracle의 비용은 = 8이 됩니다.
애플리케이션을 통해 동일한 삽입 패턴을 사용해도 성능이 거의 1.5배 향상되었습니다.
성능을 더욱 향상시킬 수 있는 방법에 대한 통찰력이 필요합니다.제가 시도한 모든 것들을 제 질문에 요약했습니다.만약 제가 관련된 것을 찾거나 발견한다면, 저는 이 질문에 추가할 것입니다.
제 목표는 천만 건의 업로드 시간을 10분 이내로 단축하는 것입니다.
다른 사용자들이 이에 대해 언급한 적이 있는 것으로 알고 있으며 이를 듣고 싶지는 않지만 SQL*Loader 또는 외부 테이블을 사용합니다.너비가 거의 같은 테이블의 평균 로드 시간은 10m가 조금 넘는 행에 대해 12.57초입니다.이러한 유틸리티는 데이터를 데이터베이스에 신속하게 로드하도록 명시적으로 설계되었으며 매우 유용합니다.입력 파일의 형식에 따라 추가 시간 페널티가 발생할 수 있지만, 옵션이 상당히 많아 로드하기 전에 파일을 변경해야 하는 경우가 거의 없습니다.
이 작업을 수행할 필요가 없다면 하드웨어를 업그레이드할 필요가 없습니다. 하드웨어를 신속하게 로드하는 데 방해가 될 수 있는 모든 장애물을 제거해야 합니다.항목을 열거하려면 다음을 제거합니다.
- 색인
- 방아쇠
- 시퀀스
- 파티션
이러한 모든 작업을 통해 데이터베이스는 더 많은 작업을 수행해야 하며 트랜잭션을 수행하기 때문에 데이터베이스를 최대한 활용하지 못합니다.
다음과 같이 데이터를 별도의 테이블에 로드합니다.ABC_LOAD
데이터가 완전히 로드되면 ABC에 단일 INSERT 문을 수행합니다.
insert into abc
select abc_seq.nextval, a.*
from abc_load a
이 작업을 수행하는 경우(그렇지 않은 경우에도) 시퀀스 캐시 크기가 올바른지 확인합니다. 다음을 참조하십시오.
응용 프로그램이 시퀀스 캐시의 시퀀스에 액세스하면 시퀀스 번호가 빠르게 읽힙니다.그러나 응용 프로그램이 캐시에 없는 시퀀스에 액세스하는 경우 시퀀스 번호를 사용하기 전에 디스크에서 캐시로 시퀀스를 읽어들여야 합니다.
응용프로그램이 여러 시퀀스를 동시에 사용하는 경우 시퀀스 캐시가 모든 시퀀스를 저장할 수 있을 정도로 크지 않을 수 있습니다.이 경우 시퀀스 번호에 액세스하려면 디스크 읽기가 필요할 수 있습니다.모든 시퀀스에 빠르게 액세스하려면 캐시에 응용 프로그램에서 동시에 사용하는 모든 시퀀스를 저장할 수 있는 항목이 충분해야 합니다.
즉, 10개의 스레드가 이 시퀀스를 사용하여 각각 500개의 레코드를 동시에 작성하는 경우 5,000개의 캐시 크기가 필요합니다.ALTER SEQUENCE 문서에는 변경 방법이 나와 있습니다.
alter sequence abc_seq cache 5000
만약 당신이 제 제안을 따른다면, 저는 캐시 크기를 10.5m 정도로 하겠습니다.
ADPEND 힌트(Oracle Base 참조)를 사용하여 데이터를 넣을 공간을 찾지 않고 테이블 끝에 직접 데이터를 추가하는 직접 경로 삽입을 사용하도록 Oracle에 지시합니다.테이블에 인덱스가 있지만 사용할 수 있는 경우 이 기능을 사용할 수 없습니다.ABC_LOAD
insert /*+ append */ into ABC (SSM_ID, invocation_id , calc_id, ... )
select 'c','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'a','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'b','b',NULL, 'test', 123 , 'N', 'asdf' from dual
union all select 'c','g',NULL, 'test', 123 , 'N', 'asdf' from dual
추가 힌트를 사용하면 잘라내기를 추가합니다. ABC_LOAD
에삽한후에 ABC
그렇지 않으면 이 테이블은 무한히 커질 것입니다.그때까지 테이블 사용을 마치셨으니 안전할 것입니다.
사용 중인 버전이나 버전 또는 Oracle에 대해서는 언급하지 않았습니다.사용할 수 있는 추가적인 작은 트릭은 다음과 같습니다.
오라클 12c
이 버전은 ID 열을 지원하므로 시퀀스를 완전히 제거할 수 있습니다.
CREATE TABLE ABC( seq_no NUMBER GENERATED AS IDENTITY (increment by 5000)
오라클 11gr2
트리거를 유지하는 경우 시퀀스 값을 직접 할당할 수 있습니다.
:new.seq_no := ABC_seq.nextval;
Oracle Enterprise Edition
Enterprise에서 를 높일 수 .
ABC_LOAD
병렬 힌트를 사용하여:insert /*+ parallel */ into abc select abc_seq.nextval, a.* from abc_load a
이로 인해 자체적인 문제(병렬 프로세스가 너무 많음 등)가 발생할 수 있으므로 테스트하십시오.작은 배치 삽입에 도움이 될 수도 있지만 어떤 스레드를 처리해야 하는지 계산하는 데 시간을 낭비할 가능성이 적습니다.
tl;dr
데이터베이스와 함께 제공되는 유틸리티를 사용합니다.
만약 당신이 그것들을 사용할 수 없다면 삽입 속도를 늦출 수 있는 모든 것을 제거하고 대량으로 수행하십시오. 왜냐하면 그것이 데이터베이스가 잘하는 일이기 때문입니다.
텍스트 파일이 있는 경우 직접 경로로 SQL LOADER를 사용해 보십시오.이는 매우 빠르고 이러한 종류의 대규모 데이터 로드를 위해 설계되었습니다.성능을 향상시킬 수 있는 이 옵션을 살펴보십시오.
ETL의 2차적인 장점으로, 일반 텍스트의 파일은 10^7 삽입물보다 작고 감사하기 쉽습니다.
혁신이 필요한 경우 Oracle을 사용하여 나중에 혁신을 수행할 수 있습니다.
데이터를 대량으로 삽입해야 합니다.이를 위해 OCI*ML을 사용할 수 있습니다. 이에 대한 논의는 여기에 있습니다.주목할 만한 기사가 여기 있습니다.또는 Oracle SQL Bulk Loader를 사용해 보십시오.SQLLDR
업로드 속도를 높일 수 있습니다.이를 위해 데이터를 csv 파일로 직렬화하고 인수로 csv를 전달하는 SQLDR을 호출합니다.
또 다른 최적화 방법은 트랜잭션 전략입니다.스레드/연결당 하나의 트랜잭션에 모든 데이터를 삽입합니다.
INSERT ALL
INTO ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source ) VALUES ('c','b',NULL, 'test', 123 , 'N', 'asdf')
INTO ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source ) VALUES ('a','b',NULL, 'test', 123 , 'N', 'asdf')
INTO ABC (SSM_ID, invocation_id , calc_id, analytic_id, analytic_value,
override, update_source ) VALUES ('b','b',NULL, 'test', 123 , 'N', 'asdf')
SELECT 1 FROM DUAL;
대신insert .. union all
.
표본 데이터는 서로 독립적으로 보이며, 이는 1개의 유의한 행을 삽입한 다음 삽입 후 sql 쿼리를 사용하여 4개의 행으로 확장됩니다.
또한 배치를 삽입하기 전에 모든 인덱스를 해제하거나 삭제한 후 대량으로 다시 작성합니다.테이블 인덱스는 실제로 사용하지 않는 동안 삽입 성능을 줄입니다(삽입된 모든 행에 대해 일부 ID를 계산하고 해당 작업을 수행함).
준비된 문 구문을 사용하면 업로드 루틴 속도가 빨라집니다. 서버에 이미 구문 분석된 문이 있기 때문입니다.
그런 다음 C++ 코드를 최적화합니다. ops를 주기 밖으로 이동합니다.
//! Preparing the Query
std::string insert_query = "insert into ";
insert_query += Context::instance().getUpdateTable();
insert_query += " (SSM_ID, invocation_id , calc_id,
analytic_id, analytic_value, override, update_source)\n";
while (startOffset < statements.size())
{ ... }
그런데, 스레드뿐만 아니라 물리적 클라이언트의 수를 늘리려고 노력했습니까?여러 VM 또는 여러 물리적 시스템에서 클라우드에서 실행됩니다.저는 최근 에어로스피크 개발자들의 의견을 읽었는데, 많은 사람들이 단지 고객이 실제로 초당 그렇게 많은 쿼리를 전송하도록 하는 것이 그렇게 쉽지 않다는 것을 이해하지 못하기 때문에 결과를 재현할 수 없다고 설명했습니다(그들의 경우 초당 1M 이상).예를 들어 벤치마크의 경우 4개의 클라이언트를 병렬로 실행해야 했습니다.이 특정 오라클 드라이버는 단일 머신에서 초당 7-8,000건 이상의 요청을 지원할 만큼 충분히 빠르지 않은 것이 아닐까요?
언급URL : https://stackoverflow.com/questions/24734131/insert-of-10-million-queries-under-10-minutes-in-oracle
'sourcecode' 카테고리의 다른 글
json 인코딩된 폼 오류를 심포니로 반환하는 방법 (0) | 2023.07.28 |
---|---|
자바.java.java잘못된 상태 예외:'classpath:/application.yml' 위치에서 속성 소스를 로드하지 못했습니다. (0) | 2023.07.28 |
RecyclerView 스크롤을 비활성화하는 방법은 무엇입니까? (0) | 2023.07.28 |
Ajax HTML 응답에서 본문 태그 찾기 (0) | 2023.07.28 |
mariadb의 select+update 트랜잭션을 원자적으로 실행할 수 없습니다. (0) | 2023.07.28 |