oracle_logo

Olá a todos. Nesta terceira parte, falarei um pouco sobre como otimizar o uso da rede, no escopo do banco de dados Oracle. Antes, gostaria de esclarecer que não sou uma profundo conhecedor de redes, logo, mencionarei aqui algumas dicas simples para tirar o melhor proveito da relação Oracle x Rede.

1) Aumentando o throughput da rede

A Oracle sugere definir os parâmetros RECV_BUF_SIZE e SEND_BUF_SIZE (valores em bytes), dentro do tnsnames.ora e do listener.ora. Abaixo segue uma nota de um doc da Oracle sobre esses 2 parâmetros:

      “Reliable network protocols like TCP/IP buffer data into send and receive buffers while sending and receiving to or from lower and upper layer protocols. The sizes of these buffers affect network performance, as these buffer sizes influence flow control decisions.

      The RECV_BUF_SIZE and SEND_BUF_SIZE parameters specify sizes of socket receive and send buffers, respectively, associated with an Oracle Net connection.

      To ensure the continuous flow of data and better utilization of network bandwidth, specify the I/O buffer space limit for receive and send operations of sessions with the RECV_BUF_SIZE and SEND_BUF_SIZE parameters.

      For best performance, the size of the send and receive buffers should be set large enough to hold all of the data that may be sent concurrently on the network connection. For a simple database connection, this typically maps to the OCI_PREFETCH_MEMORY size.

      Setting the SEND_BUF_SIZE and RECV_BUF_SIZE to at least the bandwidth-delay product, will insure that when large amounts of data are being sent that the network bandwidth will be optimally utilized.”

Exemplo:


### tnsnames.ora
DBCLONE =
(DESCRIPTION =
(SDU = 32767)
(SEND_BUF_SIZE = 2092500)
(RECV_BUF_SIZE = 2092500)
(ADDRESS = (PROTOCOL = TCP)(HOST = dg1.localdomain)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = dbclone.world)
)
)


### listener.ora
LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(SDU=32767)
(SEND_BUF_SIZE=2092500)
(RECV_BUF_SIZE=2092500)
(ADDRESS = (PROTOCOL = TCP)(HOST = dg1.localdomain)(PORT = 1521))
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
)
)

Já usei essas configurações apenas em ambientes de Dataguard.

 

2) Replicação de dados usando Materialized Views

Usar Mviews é melhor do que replicar dados via db_links. É melhor selecionar localmente dados préviamente replicados via materialized view, do que selecionar esses dados, sob demanda, via db_links.

Pode-se criar mviews na base destino, buscando dados, via dblinks, na base origem. É possível agendar o refresh da mview para ser realizar de tempos em tempos (de 5 em 5 minutos, por exemplo). Esse refresh deve ser FAST, para que apenas os dados modificados desde o último refresh sejam retornados para a base destino.

Replicação de dados, com a finalidade de obter melhor performance, é mais eficiênte quando os dados de origem são pouco modificados ou quando os processos de negócio aceitam trabalhar com dados um pouco defazados.

 

3) Usando Remote Procedure Calls (RPC)

É mais vantajoso chamar uma procedure remota do que criar uma procedure localmente que execute ações em bases remotas. Ex:


-- menos vantajoso, pois há um aumento do tráfego de rede nesse caso
CREATE PROCEDURE STP_LOCAL (PARAM IN VARCHAR2) IS

BEGIN

UPDATE NOME_TABELA@DBLINK
SET COLUNA = 123
WHERE OUTRA_COLUNA = PARAM;

END;

-- mais vantajoso, pois todo o processamento será feito na base de origem, e o que trafegará via rede
-- é apenas o resultado da execução da procedure
EXEC STP_LOCAL@DBLINK(PARAMETRO);

 

4) Especificar as colunas a serem buscadas

É sempre recomendado especificar no select quais serão as colunas retornadas pela query. Isso evita que dados sejam retornados sem necessidade, reduzindo I/O e rede. Ex:

Tabela...........: FABRICIO.TAB_TESTES_CARGA (2.000.000 de linhas)
Coluna Indexada..: ID_REGISTRO

-- caso se necessite apenas do ID_REGISTRO, executar a query abaixo, retornando tudo, altera o plano de execução
-- no exemplo abaixo, um FULL-TABLE SCAN ocorre

SELECT * FROM FABRICIO.TAB_TESTES_CARGA
WHERE ID_REGISTRO BETWEEN 1 AND 1000

--------------------------------------------------------------------------------------
| Id  | Operation         | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                  | 24955 |  1632K|  8077   (1)| 00:01:37 |
|*  1 |  TABLE ACCESS FULL| TAB_TESTES_CARGA | 24955 |  1632K|  8077   (1)| 00:01:37 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("ID_REGISTRO"<=1000 AND "ID_REGISTRO">=1)

Estatisticas
----------------------------------------------------------
0  recursive calls
0  db block gets
31946  consistent gets
30417  physical reads
0  redo size
1759021  bytes sent via SQL*Net to client
17256  bytes received via SQL*Net from client
1533  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
22969  rows processed

 

-- no exemplo abaixo, ocorre o uso do indice (index-range scan)

SELECT ID_REGISTRO FROM FABRICIO.TAB_TESTES_CARGA
WHERE ID_REGISTRO BETWEEN 1 AND 1000

--------------------------------------------------------------------------------------------
| Id  | Operation        | Name                    | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |                         | 24955 |   121K|    50   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| IDX_ID_REG_TESTES_CARGA | 24955 |   121K|    50   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - access("ID_REGISTRO">=1 AND "ID_REGISTRO"<=1000)

Estatisticas
----------------------------------------------------------
0  recursive calls
0  db block gets
1569  consistent gets
0  physical reads
0  redo size
313627  bytes sent via SQL*Net to client
17256  bytes received via SQL*Net from client
1533  SQL*Net roundtrips to/from client
0  sorts (memory)
0  sorts (disk)
22969  rows processed

É isso ai, por enquanto, pessoal. São poucas dicas, mas que podem ser de alguma utilidade. Como havia dito anteriormente, redes não é muito o meu forte, por isso, quando você precisar realizar alguma operação de tuning de redes, faça isso em conjunto com seu administrador de redes.

Keep Tuning