Departamento de
Universidade de Aveiro Electrónica, Telecomunicações e Informática
2011
Wilson Bertino
Lopes dos Santos
JDBC (Java DB Connectivity) Concorrente
Departamento de
Universidade de Aveiro Electrónica, Telecomunicações e Informática
2011
Wilson Bertino
JDBC (Java DB Connectivity) Concorrente
Lopes dos Santos
Dissertação apresentada à Universidade de Aveiro para cumprimento dos
requisitos necessários à obtenção do grau de Mestre em Engenharia de
Computadores e Telemática, realizada sob a orientação cientı́fica do Dr.
Diogo Nuno Pereira Gomes, Assistente Convidado do Departamento de
Electrónica, Telecomunicações e Informática da Universidade de Aveiro e
do Mestre Óscar Narciso Mortágua Pereira, Assistente Convidado do Departamento de Electrónica, Telecomunicações e Informática da Universidade
de Aveiro
o júri / the jury
presidente / president
Prof. Dr. Rui Luı́s Andrade Aguiar
Professor Associado da Universidade de Aveiro
vogais / examiners committee
Prof. Dra. Maribel Yasmina Campos Alves Santos
Professora Auxiliar do Dep. de Sistemas de Informação da Universidade do Minho
(arguente principal)
Dr. Diogo Nuno Pereira Gomes
Assistente Convidado da Universidade de Aveiro (orientador)
Mestre Óscar Narciso Mortágua Pereira
Assistente Convidado da Universidade de Aveiro (co-orientador)
agradecimentos /
Os meus agradecimentos vão para os meus pais, cujo esforço e dedicação
acknowledgements
permitiu-me atingir e superar esta etapa.
Resumo
A API JDBC permite aos programas Java manipularem dados de uma base
de dados. No entanto, a definição da API não prevê uma utilização concorrente dos seus serviços, não é por isso possı́vel partilhar objectos JDBC em
segurança entre threads.
Neste documento é descrita uma implementação concorrente da interface
ResultSet. Esta interface é utilizada para ler ou modificar linhas do resultado
da execução de uma instrução SQL. O driver JDBC foi criado para SQL
Server 2008.
De modo a avaliar o desempenho da solução desenvolvida foram realizados
testes de desempenho comparando-a com a implementação do driver da Microsoft, em que se criou um ResultSet por thread. Os resultados mostraram
que a ideia desenvolvida produz um aumento de desempenho em ambientes
multithreaded.
Abstract
The JDBC API allows Java programs to access data stored on a data base.
However, the API specification doesn’t provide a solution for concurrent
access to its interfaces, so it isn’t safe to shared the same JDBC object
between threads.
This document describes the concurrent implementation of the Result Set
interface. This interface is used to read or modify lines in the result of
executing a SQL statement. The JDBC driver was created for SQL Server
2008.
In order to assess its performance, the developed solution was benchmarked
against the situation where it is created one ResultSet per thread using
Microsoft’s implementation of the JDBC driver. Results show that the
solution increases performance on a multithreaded environment.
Conteúdo
Conteúdo
iii
Lista de tabelas
v
Lista de figuras
viii
1 Introdução
1
1.1
Integração de Linguagens de programação e Bases de dados . . . . . . . . . .
2
1.2
O que é JDBC? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
1.3
O que é um JDBC driver? . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.4
Breve tutorial JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1.4.1
Instalação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1.4.2
Criar uma ligação . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
1.4.3
Executar uma query . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
Motivação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
1.5
2 Implementação de um driver JDBC
2.1
2.2
13
Camada TDS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
2.1.1
TDSMessage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
2.1.2
ITDSResultSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
Camada JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
3 Arquitectura do ResultSet Concorrente
3.1
3.2
3.3
19
Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
19
3.1.1
Problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
3.1.2
Ideia base da solução . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
ResultSet Wrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
21
3.2.1
Utilização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
22
Cursor Concorrente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
3.3.1
Anatomia de um ResultSet . . . . . . . . . . . . . . . . . . . . . . . .
24
3.3.2
Cache individual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
3.3.3
Cache partilhado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
3.3.4
Utilização . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
i
4 Benchmark
31
4.1
Benchmark principal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
4.2
Benchmark com atrasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
4.3
Cache individual vs Cache partilhado . . . . . . . . . . . . . . . . . . . . . . .
38
5 Resultados
39
5.1
Plataforma de teste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
39
5.2
Benchmark principal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
40
5.2.1
Comparação com MSJDBC . . . . . . . . . . . . . . . . . . . . . . . .
40
5.2.2
Comparação com WJDBC . . . . . . . . . . . . . . . . . . . . . . . . .
48
5.2.3
Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
54
Benchmark com atrasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
54
5.3.1
Atraso entre colunas . . . . . . . . . . . . . . . . . . . . . . . . . . . .
54
5.3.2
Atraso entre linhas . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
62
5.3.3
Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
69
Cache individual vs Cache partilhado . . . . . . . . . . . . . . . . . . . . . . .
69
5.4.1
Fetch size 10% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
5.4.2
Fetch size 20% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
70
5.4.3
Fetch size 50% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
5.4.4
Fetch size 75% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
5.4.5
Fetch size 100% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
5.4.6
Resumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
74
5.3
5.4
6 Discussão
6.1
75
Análise de resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
75
6.1.1
Comparação com JDBC . . . . . . . . . . . . . . . . . . . . . . . . . .
75
6.1.2
Comparação com WJDBC . . . . . . . . . . . . . . . . . . . . . . . . .
78
6.1.3
Comparação com atrasos . . . . . . . . . . . . . . . . . . . . . . . . .
79
6.1.4
Cache individual vs Cache partilhado . . . . . . . . . . . . . . . . . .
80
6.2
Conclusão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
80
6.3
Trabalho relacionado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81
6.4
Trabalho Futuro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81
Glossário
84
Acrónimos
85
Bibliografia
87
A Estudo do SQLServerResultSet
93
A.1 Cursores no SQL Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
93
A.1.1 Fetching e Scrolling . . . . . . . . . . . . . . . . . . . . . . . . . . . .
93
A.1.2 Concorrência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
94
ii
A.1.3 Tipos de cursor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
95
A.2 Tipos de cursor por result set . . . . . . . . . . . . . . . . . . . . . . . . . . .
95
A.3 Adaptive Buffering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
98
B Tabular Data Stream
101
B.1 Mensagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
B.2 Pacotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
B.2.1 Cabeçalho . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
B.2.2 Zona de dados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
B.3 Tokenless Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
B.3.1 Pre-Login . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
B.3.2 Login . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
B.3.3 SQLBatch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
B.4 Token Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
C Cursor Stored Procedures
C.1 sp cursor
107
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
C.1.1 Sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
C.1.2 Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
C.2 sp cursoropen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
C.2.1 Sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
C.2.2 Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
C.3 sp cursorfetch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
C.3.1 Sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
C.3.2 Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
C.4 sp cursorclose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
C.4.1 Sintaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
C.4.2 Argumentos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
D Funcionalidade implementada
113
D.1 Implementação JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
D.2 Implementação TDS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
iii
iv
Lista de Tabelas
1.1
Serviços do ResultSet que permitem mover o cursor. . . . . . . . . . . . . . .
8
1.2
Serviços do ResultSet que permitem modificar os dados do dataset. . . . . . .
9
2.1
Descrição dos serviços da classe TDSMessage . . . . . . . . . . . . . . . . . .
15
2.2
Descrição dos serviços da interface ITDSResultSet . . . . . . . . . . . . . . .
16
2.3
Interfaces da API JDBC implementadas. . . . . . . . . . . . . . . . . . . . . .
17
3.1
Descrição dos atributos da implementação do ResultSet. . . . . . . . . . . . .
24
3.2
Métodos reimplementados pela classe CursorIndividualCache. . . . . . . . . .
27
3.3
Métodos reimplementados pela classe CursorSharedCache . . . . . . . . . . .
28
A.1 Tipos de cursor suportados pelo driver . . . . . . . . . . . . . . . . . . . . . .
96
A.1 Tipos de cursor suportados pelo driver . . . . . . . . . . . . . . . . . . . . . .
97
A.1 Tipos de cursor suportados pelo driver . . . . . . . . . . . . . . . . . . . . . .
98
B.1 Campos do cabeçalho TDS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
B.2 Indicação das mensagens que usam tokens . . . . . . . . . . . . . . . . . . . . 104
B.3 Opções da mensagem de Pre-Login . . . . . . . . . . . . . . . . . . . . . . . . 105
B.4 Packet Data Token Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
C.1 Stored prodecures do sistema relevantes para a implementação do driver JDBC. 107
D.1 Métodos implementados da interface Driver . . . . . . . . . . . . . . . . . . . 113
D.2 Métodos implementados da interface Statement . . . . . . . . . . . . . . . . . 113
D.3 Métodos implementados da interface ResultSet . . . . . . . . . . . . . . . . . 114
D.4 Tipos SQL suportados pelo driver. . . . . . . . . . . . . . . . . . . . . . . . . 115
v
vi
Lista de Figuras
1.1
Arquitectura JDBC
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
2.1
Arquitectura de um driver JDBC do tipo 4 para SQL Server 2008. . . . . . .
14
2.2
Classe TDSMessage. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15
2.3
Interface ITDSResultSet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
3.1
Representação da relação entre o ResultSet, o cursor servidor e o dataset . . .
19
3.2
Relação de muitos para um entre o ResultSet e o cursor do servidor. . . . . .
20
3.3
Diagrama de classes da solução ResultSet Wrapper. . . . . . . . . . . . . . . .
21
3.4
Diagrama das classes envolvidas na utilização das solução Cursor Wrapper. .
23
3.5
Visão geral da implementação do ResultSet . . . . . . . . . . . . . . . . . . .
24
3.6
Diagrama de classes do cursor com cache individual. . . . . . . . . . . . . . .
26
3.7
Diagrama de classes da implementação do cursor com cache partilhado. . . .
28
3.8
Classes principais na utilização da solução Cursor. . . . . . . . . . . . . . . .
30
4.1
Tabela utilizada no benchmark. . . . . . . . . . . . . . . . . . . . . . . . . . .
34
4.2
Medição do tempo de preparação . . . . . . . . . . . . . . . . . . . . . . . . .
34
4.3
Medição do tempo de execução . . . . . . . . . . . . . . . . . . . . . . . . . .
35
5.1
M SJDBC/CJDBCI , no contexto Actualização. . . . . . . . . . . . . . . .
40
5.2
M SJDBC/CJDBCS , no contexto Actualização. . . . . . . . . . . . . . . .
41
5.3
M SJDBC/W JDBC, no contexto Actualização. . . . . . . . . . . . . . . .
42
5.4
M SJDBC/CJDBCI , no contexto Leitura. . . . . . . . . . . . . . . . . . .
42
5.5
M SJDBC/CJDBCS , no contexto Leitura. . . . . . . . . . . . . . . . . . .
43
5.6
M SJDBC/W JDBC, no contexto Leitura. . . . . . . . . . . . . . . . . . . .
44
5.7
M SJDBC/CJDBCI , no contexto Inserção. . . . . . . . . . . . . . . . . . .
44
5.8
M SJDBC/CJDBCS , no contexto Inserção. . . . . . . . . . . . . . . . . . .
45
5.9
M SJDBC/W JDBC, no contexto Inserção. . . . . . . . . . . . . . . . . . .
46
5.10 M SJDBC/CJDBCI , no contexto Remoção. . . . . . . . . . . . . . . . . .
46
5.11 M SJDBC/CJDBCS , no contexto Remoção. . . . . . . . . . . . . . . . . .
47
5.12 M SJDBC/W JDBC, no contexto Remoção. . . . . . . . . . . . . . . . . .
48
5.13 W JDBC/CJDBCI , no contexto Actualização. . . . . . . . . . . . . . . . .
49
5.14 W JDBC/CJDBCS , no contexto Actualização. . . . . . . . . . . . . . . . .
50
5.15 W JDBC/CJDBCI , no contexto Leitura. . . . . . . . . . . . . . . . . . . .
50
vii
5.16 W JDBC/CJDBCS , no contexto Leitura. . . . . . . . . . . . . . . . . . . .
51
5.17 W JDBC/CJDBCI , no contexto Inserção. . . . . . . . . . . . . . . . . . . .
51
5.18 W JDBC/CJDBCS , no contexto Inserção.
. . . . . . . . . . . . . . . . . .
52
5.19 W JDBC/CJDBCI , no contexto Remoção. . . . . . . . . . . . . . . . . . .
53
5.20 W JDBC/CJDBCS , no contexto Remoção. . . . . . . . . . . . . . . . . . .
53
5.21 M SJDBC/CJDBCI , efeito do atraso no contexto Actualização. . . . . . .
55
5.22 M SJDBC/CJDBCS , efeito do atraso no contexto Actualização. . . . . . .
56
5.23 M SJDBC/W JDBC, efeito do atraso no contexto Actualização. . . . . . .
56
5.24 M SJDBC/CJDBCI , efeito do atraso no contexto Leitura. . . . . . . . . .
57
5.25 M SJDBC/CJDBCS , efeito do atraso no contexto Leitura. . . . . . . . . .
58
5.26 M SJDBC/W JDBC, efeito do atraso no contexto Leitura. . . . . . . . . .
58
5.27 W JDBC/CJDBCI , efeito do atraso no contexto Actualização. . . . . . . .
59
5.28 W JDBC/CJDBCS , efeito do atraso no contexto Actualização. . . . . . . .
60
5.29 W JDBC/CJDBCI , efeito do atraso no contexto Leitura. . . . . . . . . . .
61
5.30 W JDBC/CJDBCS , efeito do atraso no contexto Leitura. . . . . . . . . . .
61
5.31 M SJDBC/CJDBCI , efeito do atraso no contexto Actualização. . . . . . .
62
5.32 M SJDBC/CJDBCS , efeito do atraso no contexto Actualização. . . . . . .
63
5.33 M SJDBC/W JDBC, efeito do atraso no contexto Actualização. . . . . . .
64
5.34 M SJDBC/CJDBCI , efeito do atraso no contexto Leitura. . . . . . . . . .
64
5.35 M SJDBC/CJDBCS , efeito do atraso no contexto Leitura. . . . . . . . . .
65
5.36 M SJDBC/W JDBC, efeito do atraso no contexto Leitura. . . . . . . . . .
66
5.37 W JDBC/CJDBCI , efeito do atraso no contexto Actualização. . . . . . . .
67
5.38 W JDBC/CJDBCS , efeito do atraso no contexto Actualização. . . . . . . .
67
5.39 W JDBC/CJDBCI , efeito do atraso no contexto Leitura. . . . . . . . . . .
68
5.40 W JDBC/CJDBCS , efeito do atraso no contexto Leitura. . . . . . . . . . .
69
5.41 CJDBCI /CJDBCS M , no contexto 10 . . . . . . . . . . . . . . . . . . . . .
70
5.42 CJDBCI /CJDBCS M , no contexto 20. . . . . . . . . . . . . . . . . . . . . .
71
5.43 CJDBCI /CJDBCS M , no contexto 50. . . . . . . . . . . . . . . . . . . . . .
71
5.44 CJDBCI /CJDBCS M , no contexto 75. . . . . . . . . . . . . . . . . . . . . .
72
5.45 CJDBCI /CJDBCS M , no contexto 100. . . . . . . . . . . . . . . . . . . . .
73
5.46 CJDBCI /CJDBCS , no contexto 100. . . . . . . . . . . . . . . . . . . . . . .
73
6.1
Comparação entre CJDBC e M SJDBC, no contexto Leitura
. . . . . . . . . .
77
6.2
Comparação entre CJDBC e M SJDBC, no contexto Actualização
. . . . . . .
77
viii
Capı́tulo 1
Introdução
O objectivo deste trabalho é realizar uma implementação concorrente de um driver JDBC
para SQL Server 2008.
Este primeiro capı́tulo começa por referir o problema da integração de linguagens de
programação e bases de dados, explicando a razão da escolha do JDBC como solução. É
fornecida uma descrição mais detalhada do que consiste o JDBC, incluindo um pequeno
tutorial exemplificando como se pode utilizar a API JDBC para aceder a dados numa base
de dados. Este capı́tulo explica também o que é um driver JDBC. Por fim são identificados
os problemas que a API JDBC apresenta no âmbito da execução de código concorrente, e
que estão na base da motivação para a realização deste trabalho.
No capı́tulo 2 é explicada a arquitectura da implementação do driver JDBC para SQL
Server 2008.
No capı́tulo 3 são apresentadas as soluções para o problema da partilha de objectos JDBC
e é explorado em maior profundidade o desenvolvimento do driver JDBC, no que se refere à
implementação do ResultSet.
No capı́tulo 4 é descrito o método experimental utilizado para avaliar o desempenho das
soluções desenvolvidas.
No capı́tulo 5 apresentam-se os resultados obtidos.
No capı́tulo 6 analisam-se e discutem-se os resultados. São apresentadas as conclusões, é
analisado o trabalho relacionado e são indicadas sugestões para trabalhos futuros.
No apêndice A é apresentado um estudo da implementação de um ResultSet para SQL
Server. Este apêndice revela conceitos importantes relacionados com o que envolve implementar um ResultSet, e reunindo o que se aprendeu através do estudo do driver da Microsoft.
No apêndice B é descrito o protocolo de comunicação entre aplicações cliente e o SQL
Server, conhecido como Tabular Data Stream.
No apêndice C são apresentados os stored procedures que existem no SQL Server e que
são utilizados pelo ResultSet para manipular os dados do dataset.
No apêndice D é listada a funcionalidade da API JDBC que foi implementada neste
trabalho.
1
1.1
Integração de Linguagens de programação e Bases de dados
A integração de bases de dados e linguagens de programação é um problema que existe quase há 50 anos [8] e que é conhecido por impedance mismatch [8, 62, 2]. Uma das
principais razões da existência deste problema é o facto de estas duas entidades terem sido
desenvolvidas independentemente, durante muitos anos [3]. E como consequência surgiram
incompatibilidades na interoperabilidade entre a interface das linguagens procedimentais e a
interface das linguagens query das bases de dados. Exemplos dessas incompatibilidades são
programas imperativos versus queries declarativas, optimização ao nı́vel da compilação versus optimização ao nı́vel da query, algoritmos e estruturas de dados versus relações e ı́ndices,
threads versus transações, ponteiros nulos versus nulo como ausência de dados [8].
Desde sempre, a generalidade dos programas necessitou de alguma forma de dados permanentes. Alguns programas implementam sistemas de armazenamento especı́ficos, mas a
verdade é que existem diversos sistemas cuja principal função é a gestão eficiente dos dados.
Os Relational Database Management Systems (RBMS) constituem soluções aceites e bem
estabelecidas para essa função, e que apesar de surgirem soluções no âmbito de bases de
dados orientadas a objectos, crê-se que os sistemas de base de dados relacionais continuem a
existir por muitos mais anos [7]. Dois exemplos de popularidade são a Oracle Database e o
SQL Server da Microsoft [74]. Existe assim, a necessidade de encontrar uma solução para a
integração de linguagens de programação e bases de dados.
Têm sido realizados vários esforços para integrar linguagens de programação e bases de
dados. Exemplos são a exploração de linguagens de programação especializadas em base de
dados, persistência ortogonal, bases de dados orientadas a objectos, modelos de transação,
bibliotecas de acesso a dados, embedded queries, e mapeamento objecto-relacional [8].
A persistência ortogonal consiste em estender a existência de um objecto para além do
tempo de duração da execução de um programa [5]. Um dos problemas da persistência
ortogonal é que não dá espaço a optimizações [8]. PJama [45] e OPJ [4, 70] são exemplos de
persistência ortogonal.
Como alternativa à persistência ortogonal existe a execução explı́cita de queries. A Call
Level Interface (CLI) [79] é um mecanismo predominante na execução explı́cita de queries,
e permite à linguagem de programação o acesso ao database engine a partir de uma API
estandardizada. Um dos principais problemas da CLI é a ausência de tipagem estática, o que
causa a detecção de erros apenas em runtime. No entanto, permite melhorar o desempenho
geral através da redução da latência na comunicação [8]. ODBC e JDBC são dois exemplos.
As embedded queries constituem outro mecanismo de execução explı́cita de queries. Neste
mecanismo as instruções (statements SQL) são escritas directamente no código fonte da
linguagem de programação. Este mecanismo está a deixar de ser suportado por diversos
sistemas, como por exemplo o Microsoft SQL Server [22] e Sybase [78].
Apesar das inúmeras soluções que surgiram e continuam a surgir, ainda não se chegou
a um consenso na escolha de uma solução definitiva para o problema da integração. Na
2
escolha da utilização de uma das soluções, existem alguns factores a considerar tais como a
portabilidade e o desempenho.
A linguagem Java permite escrever aplicações independentes da plataforma, para sistemas
computacionais fixos ou móveis, tendo por isso um elevado nı́vel de portabilidade. A optimização ao nı́vel da Java Virtual Machine, torna a linguagem uma solução forte também
no âmbito do desempenho [1]. Para além disso, sendo o JDBC uma CLI, permite o acesso
directo ao database engine, dando flexibilidade para outras opções de desempenho.
A generalização da utilização da linguagem Java e a existência de vários Relational
Database Management Systems com raı́zes profundas, faz do JDBC um objecto de alvo
estudo.
1.2
O que é JDBC?
A JDBC (Java Database Connectivity API ) é uma Application Programming Interface
(API) para acesso a bases de dados. A JDBC é uma SQL-level API [77] o que significa que
é possı́vel construir statements SQL e introduzi-las em chamadas a código Java. Isso faz
com que se esteja praticamente a utilizar SQL e ao mesmo tempo tem-se acesso ao mundo
orientado a objectos em que os resultados dos pedidos à base de dados são objectos Java e
os problemas de acesso são resolvidos com a gestão de excepções. O objectivo principal do
JDBC é funcionar de modo simples e flexı́vel.
A JDBC não foi a primeira tentativa de uma solução para o acesso universal a bases de
dados. Uma das outras soluções que se destacam é a Open DataBase Connectivity (ODBC)
[69, 30], que também tem como principal objectivo fornecer uma interface uniforme de acesso
a bases de dados. No entanto sofre de um pouco de excesso de complexidade [77].
O desenvolvimento da JDBC foi influenciado pelas APIs já existentes, tais como a ODBC
e a X/Open SQL Call Level Interface (CLI), e foi tido o cuidado de reutilizar as principais
abstracções existentes nessas APIs, com o intuito de melhorar a aceitação por parte dos
fabricantes de base de dados, e aproveitar o conhecimento já existente dos utilizadores de
ODBC e SQL CLI [77].
A API JDBC é definida em dois pacotes Java [53, 75]:
• java.sql[54] fornece a API de acesso aos dados (normalmente guardados numa base de
dados relacional). É neste pacote que se encontram as classes mais usadas: Connection,
ResultSet, Statement e PreparedStatement.
• javax.sql[55] fornece a API de acesso aos serviços do servidor. Este pacote fornece
serviços para J2EE, tais como DataSouce e RowSet.
Entre as vantagens da JDBC destacam-se a possibilidade de utilizar os sistemas de base de
dados já existentes, a facilidade de aprendizagem, simplicidade na instalação e de manutenção
barata. Não há a necessidade de configurações de rede. O URL JDBC contém toda a
informação necessária para estabelecer a ligação [52].
3
1.3
O que é um JDBC driver?
Um JDBC driver é um componente de software que implementa a API JDBC, permitindo
assim às aplicações Java interagirem com uma base de dados.
A aplicação que utiliza JDBC para aceder a uma base de dados é alheia ao modo como
são implementadas as interfaces que utiliza, isso é inteira responsabilidade do driver. A
Figura 1.1 mostra a arquitectura tı́pica do JDBC, e vem reforçar o último ponto: uma
aplicação Java utiliza o conjunto de interfaces disponibilizadas pelo JDBC, essas interfaces
são implementadas pelo driver que também se encarrega de comunicar com o sistema da base
de dados, seja ele qual for (Oracle, SQL Sever, MySQL, etc.).
Figura 1.1: Arquitectura JDBC
Cada driver JDBC é construı́do para um Database Management System (DBMS) especı́fico implementando o protocolo de comunicação de queries e resultados entre o cliente e
a base de dados.
Existem quatro categorias de driver[48, 65]:
1. JDBC-ODBC
Utiliza um driver Open Database Connectivity fazendo uma ponte para estabelecer
a comunicação. O driver JDBC converte as invocações da API JDBC em invocações
a funções ODBC. Uma vez que o ODBC depende de bibliotecas nativas do sistema
operativo em que a JVM esta a correr, este tipo de driver é dependente da plataforma.
2. Native-API
Converte os pedidos em chamadas a uma biblioteca cliente. O driver JDBC converte as
invocações a métodos da API JDBC em invocações nativas da API da base de dados.
Este tipo de driver é dependente da plataforma, e necessita que as bibliotecas estejam
instaladas no cliente.
3. Network-Protocol
Utiliza uma camada intermédia (middleware) cuja função é converter os pedidos na
linguagem (protocolo) da base de dados. A camada intermédia converte as invocações
4
a métodos da API JDBC em mensagem do protocolo da base de dados. Este tipo de
driver é escrito totalmente em código Java e é independente da plataforma porque a
camada intermédia encarrega-se das especificidades do sistema.
4. Native-Protocol
Converte os pedidos directamente no protocolo utilizado pelo Database Management
System. O seu desempenho é melhor do que o desempenho dos drivers do tipo 1 e 2
porque não existe a conversão em chamadas ODBC ou em chamadas da API da base
de dados. Este tipo de driver é escrito totalmente em código Java e é independente da
plataforma. A diferença deste tipo para o tipo 3 é que a conversão no protocolo da
base de dados é realizada no cliente, enquanto que no driver do tipo 3 a conversão é
realizada na camada intermédia.
Um driver da categoria JDBC-ODBC é conhecido como driver JDBC do Tipo 1. Um
driver da categoria Native-API é conhecido como driver JDBC do Tipo 2. Um driver da
categoria Network-Protocol é conhecido como driver JDBC do Tipo 3. Um driver da categoria
Native-Protocol é conhecido como driver JDBC do Tipo 4.
1.4
Breve tutorial JDBC
Nesta secção serão apresentados e explicados alguns exemplos de utilização do JDBC para
aceder aos conteúdos de uma base de dados relacional.
1.4.1
Instalação
A instalação de um driver JDBC é tão simples como incluir um ficheiro jar no classpath:
java -cp driver.jar:... programa
Em que driver.jar é o ficheiro jar do driver que se pretende utilizar, e programa é o nome
da classe Java principal.
Depois no programa, antes do driver poder ser utilizado é necessário criar uma nova
instância do driver utilizando:
Class.forName(nome);
Em que nome é uma String que possui o nome da classe que implementa a interface
java.sql.Driver. Este nome pode ser obtido consultando a documentação que acompanha
o driver.
Se o driver implementar a versão 4.0 da API JDBC, este último passo pode ser omitido, porque a versão 4.0 introduz o carregamento automático das classes que implementam
java.sql.Driver e que estão presentes no classpath, através do mecanismo Java SE Service
Provider [46].
5
1.4.2
Criar uma ligação
Para criar uma ligação ao servidor apenas utiliza-se o método:
Driver.getConnection(url);.
O parâmetro url é uma string no formato (note-se que o url está no formato para SQL
Server 2008)[14]:
jdbc:sqlserver://[serverName[\instanceName][:portNumber]]
[;property=value[;property=value]]
Em que:
• jdbc:sqlserver:// é o sub-protocolo, é constante e obrigatório.
• serverName é o endereço do servidor.
• instanceName é uma instância no servidor.
• portNumber é o número da porta do serviço no servidor.
• property é uma propriedade da ligação. As propriedades para SQL Server 2008 podem
ser consultadas em [34], e dois exemplos são o nome de utilizador e a password.
A Listagem 1.1 apresenta o código Java para a criação de uma ligação à base de dados
utilizando JDBC.
Listagem 1.1: Criação de um objecto da ligação à base de dados.
1
3
// O valor de url deve ser alterado de acordo com a configuração do sistema.
String url = " jdbc : sqlserver :// localhost :1433; database = AdventureWorks "
+ " ; username = admin ; password = admin " ;
Connection con = java . sql . DriverManager . getConnection ( url );
1.4.3
Executar uma query
O resultado da execução de uma statement SQL denomina-se por result set ou dataset e é
obtido invocando o método executeQuery da interface Statement. O resultado desse método
é um objecto ResultSet que fornece serviços para operar sobre as linhas do dataset, linha a
linha. Para criar um result set, temos portanto, de estabelecer uma ligação com o servidor,
criar uma statement1 e invocar o método executeQuery da statement, tal como demonstrado
na Listagem 1.2.
Listagem 1.2: Criação de um objecto result set.
2
String sql = " SELECT column1 , column2 FROM mytable " ;
Statement stmt = con . createStatement (); // con é o objecto da ligação ao servidor
ResultSet rs = stmt . executeQuery ( sql );
1
Instância de uma classe que implementa a interface Statement.
6
As operações disponı́veis pelo objecto result set2 dependem do seu tipo. A criação da
statement (linha 2 da listagem anterior) determina o tipo de result set que é criado. Existem
três versões do método createStatement[51]:
• createStatement()
• createStatement(int resultSetType, int resultSetConcurrency)
• createStatement(int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
Os parâmetros válidos para estes métodos pertencem à interface ResultSet e são descritos
a seguir.
resultSetType pode ter um dos seguintes valores:
• TYPE FORWARD ONLY O result set só pode ser percorrido numa direcção, da primeira
para a última linha, e uma linha de cada vez.
• TYPE SCROLLABLE SENSITIVE O result set pode ser percorrido em qualquer direcção, e
pode-se aceder a qualquer linha; as modificações externas são visı́veis.
• TYPE SCROLLABLE INSENSITIVE O result set pode ser percorrido em qualquer direcção,
e pode-se aceder a qualquer linha; as modificações externas não são visı́veis.
resultSetConcurrency pode ter um dos seguintes valores:
• CONCUR READ ONLY O result set só suporta operações de leitura.
• CONCUR UPDATABLE O result set também suporta operações de modificação.
resultSetHoldability pode ter um dos seguintes valores:
• HOLD CURSORS OVER COMMIT Os cursores continuam abertos após a instrução de commit.
• CLOSE CURSORS AT COMMIT Os cursores são fechados após a instrução de commit.
O método createStatement sem argumentos cria por pré-definição um result set do tipo
TYPE FORWARD ONLY/CONCUR UPDATABLE.
Por exemplo para criar um result set scrollable3 actualizável e sensı́vel a actualizações
externas criamos uma statement como demonstrado na Listagem 1.3.
Listagem 1.3: Criação de um result set scrollable e actualizável.
1
Statement stmt = con . createStatement ( ResultSet . TYPE_SCROLLABLE_SENSIBLE ,
ResultSet . CONCUR_UPDATABLE );
2
Instância de uma classe que implementa a interface ResultSet.
Result set do tipo TYPE SCROLLABLE SENSITIVE ou TYPE SCROLLABLE INSENSITIVE, permitindo o acesso aleatório às suas linhas.
3
7
1.4.3.1
Ler os valores do result set
O processamento do resultado é realizado linha a linha, e acede-se a uma linha movendo
o cursor4 para essa linha.
A Tabela 1.1 descreve os serviços da interface ResultSet que permitem mover o cursor.
Tabela 1.1: Serviços do ResultSet que permitem mover o cursor.
Método
Descrição
next
move para a próxima linha.
previous
move para a linha anterior.
absolute(n)
move para a linha n.
relative(n)
move n linhas a partir da linha actual.
first
move para a primeira linha.
last
move para a última linha.
beforeFirst
move para a posição anterior à primeira linha.
afterLast
move para a posição posterior à última linha.
Quando o result set é criado o cursor encontra-se antes da primeira linha. A Listagem
1.4 exemplifica a leitura dos dados das colunas da primeira linha do result set.
Listagem 1.4: Ler a primeira linha do result set.
2
rs . next (); // mover o cursor para a próxima linha
rs . getInt (1); // ler o inteiro da primeira coluna
rs . getString (2); // ler a string da segunda coluna
Se o result set for do tipo TYPE FORWARD ONLY apenas o serviço next se encontra
disponı́vel, se um dos outros for invocado será apresentado um erro.
1.4.3.2
Modificar os valores de um result set
A interface ResultSet também permite actualizar os dados de um result set, mas a Statement que lhe dá origem tem que ser criada com o parâmetro resultSetConcurrency igual a
CONCUR UPDATABLE.
A Tabela 1.2 descreve os serviços da interface ResultSet que permitem modificar os dados
do dataset.
4
O interface ResultSet fornece o mesmo tipo de funcionalidade que um cursor[19, 20], por isso diz-que que
se está a mover o cursor quando se invoca uma operação que altera a linha em que o ResultSet se encontra.
8
Tabela 1.2: Serviços do ResultSet que permitem modificar os dados do dataset.
Método
Descrição
updateXXX(n, val)
Actualiza o valor da coluna n com o valor val. Existe
um método update para cada tipo de dados, por isso
XXX deve ser substituı́do por Int, String, Date, etc.
updateRow
Envia para o servidor as modificações realizadas à
linha.
moveToInsertRow
Move o cursor para uma linha especial que depois pode
ser enviada ao servidor para ser inserida no dataset.
insertRow
Envia para o servidor uma linha que deve ser acrescentada ao dataset.
moveToCurrentRow
Cancela a inserção da linha e move o cursor para a
linha actual.
deleteRow
Remove uma linha do dataset.
As listagens seguintes exemplificam uma operação de actualização, inserção e remoção,
respectivamente.
Listagem 1.5: Actualização de uma linha do result set.
1
3
// Criação do result set.
Statement stmt = con . createStatement ( TYPE_SCROLL_SENSITIVE ,
CONCUR_UPDATABLE );
ResultSet rs = stmt . executeQuery ( sql );
5
7
9
// Actualização do result set.
rs . absolute (5); // mover para a linha 5, será actualizada.
rs . updateInt (1 , val1 ); // actualizar a primeira coluna com o valor ”val1”
rs . updateString (2 , val2 ); // actualizar a segunda coluna com o valor ”val2”
rs . updateRow (); // enviar as modificações para o servidor
Listagem 1.6: Inserção de uma linha no result set.
// Criação igual ao exemplo anterior (...)
2
4
6
8
// Inserção de uma nova linha.
rs . moveToInsertRow (); // iniciar uma inserção.
rs . updateInt (1 , val1 );
rs . updateString (2 , val2 );
rs . insertRow (); // enviar as modificações para o servidor.
rs . moveToCurrentrow (); // mover o cursor para linha actual.
Listagem 1.7: Remoção de uma linha do result set.
// Criação igual ao exemplo anterior (...)
2
// Remoção de uma linha.
9
4
rs . last (); // mover para a última linha, que será removida.
rs . deleteRow (); // enviar as modificações para o servidor
1.5
Motivação
A motivação para o desenvolvimento deste trabalho surge do facto que o JDBC não inclui
mecanismos para tirar partido de um ambiente multihreaded.
As operações dos objectos dos pacotes java.sql e javax.sql devem ser thread-safe [58],
isto é, devem operar correctamente numa situação em que existem diversos threads a aceder
a um objecto. No entanto, apenas garantir o invariante de um objecto não é tirar partido de
um ambiente multithreaded, e há mesmo situações em que um objecto não deve ser partilhado
entre threads.
Vejamos a situação representada na Listagem 1.8, que mostra o fio de execução de dois
threads que se encontram a trabalhar em paralelo sobre o mesmo objecto (rs é uma referência
para uma instância de um ResultSet).
Listagem 1.8: Dois threads a operar em simultâneo sobre o mesmo result set.
1
3
// Thread A
rs . absolute (4);
int id = rs . getInt (1);
// Thread B
rs . next ();
int id = rs . getInt (1);
String name = rs . getString (2);
Como não há certezas em relação à ordem em que as operações serão executadas, os
resultados são imprevisı́veis. Por exemplo, a linha 2 do Thread A pode ser executada, e antes
que a sua linha 3 seja executada, a linha 2 do Thread B é executada, e a seguir o Thread
A lê o valor da linha a seguir à pretendida. Neste exemplo existe também o problema que
quando a linha 2 do Thread B foi executada pretendia-se mover para a próxima linha (a linha
anterior à chamada absolute(4)), e afinal moveu-se para a linha 5.
Vejamos só mais um problema, representado na Listagem 1.9.
Listagem 1.9: Dois threads a operar em simultâneo sobre o mesmo result set.
2
4
// Thread A
rs . updateInt (1 , ival );
rs . updateString (2 , sval );
rs . updateRow ();
// Thread B
rs . relative (5);
O Thread A está a tentar actualizar os valores de uma linha. O Thread B quer mover
o result set para uma linha que se encontra 5 linhas à frente da linha actual. O código
do Thread B pode executar antes que a chamada updateRow() seja realizada, tendo como
consequência a perda dos novos valores do Thread A.
Estas são apenas duas situações problemáticas de várias que podem surgir quando se
partilha o mesmo objecto de um ResultSet entre threads.
10
Como se resolvem estes problemas? Bem, uma solução poderia passar por criar um result set para cada thread. Mas isso implica a criação e execução de uma statement para
cada thread. É facilmente perceptı́vel que esta situação cria desperdı́cio de recursos. Outras soluções envolvem a criação de mecanismos especiais ao nı́vel da aplicação, de modo a
providenciar um ambiente realmente concorrente, ou então ser o próprio driver fornecer tais
mecanismos. Estas soluções serão discutidas no próximo capı́tulo.
11
12
Capı́tulo 2
Implementação de um driver JDBC
Este capı́tulo apresenta as linhas gerais da implementação do driver JDBC do tipo 4 para
o Database Management System Microsoft SQL Server 2008 [9]. A informação aqui contida
refere-se particularmente ao driver desenvolvido neste trabalho.
Apesar do JDBC ser uma Client Level Interface, e dar por isso a possibilidade de se
interacção directa com o database engine, este trabalho não contempla optimizações que
têm como origem a utilização de funcionalidades próprias do DBMS utilizado. Pretende-se
antes encontrar um modelo de optimização no que se refere ao acesso concorrente de objectos
JDBC partilhados. Neste caso em particular, o objectivo centra-se em partilhar um objecto
ResultSet. Encontrando um modelo de acesso que prova ser eficiente, independentemente do
database engine sobre o qual se está a trabalhar, criam-se condições para que esse modelo
seja mais largamente aceite, e possa ser utilizado na implementação de drivers JDBC para
os diversos sistemas de base de dados existentes.
O desenho e implementação deste driver é focalizado na componente cliente que o constitui, deixando a componente servidor o mais abstracta possı́vel. A única componente especı́fica referente à componente servidor, consiste na interface de comunicação com este.
Mais propriamente utiliza-se o protocolo de comunicação com o SQL Server (protocolo Tabular Data Stream (TDS)), e utiliza-se a interface programática existente para efectuar diversas
operações com um cursor do servidor, tais como a sua declaração ou a obtenção de uma linha
especı́fica do result set (conjunto de linhas seleccionadas pela statement SQL). Esta especialização da componente servidor era inevitável para se conseguir criar um driver JDBC
funcional, que possibilitasse testar o seu desempenho.
O driver implementa duas camadas: JDBC e TDS. A camada JDBC refere-se ao conjunto
de classes que implementam as interfaces definidas na API JDBC [46]. A camada TDS referese ao conjunto de classes e interfaces que implementam o protocolo de comunicação com SQL
Server, o Tabular Data Stream [11, 63].
A arquitectura geral de um driver JDBC do tipo 4 para SQL Server 2008 é apresentada
na Figura 2.1. O driver JDBC converte os pedidos de acesso à API JDBC em invocações à
camada TDS, que conhece o modo como os pedidos devem ser realizados ao SQL Server e
13
converte esses pedidos em mensagens do protocolo TDS.
Figura 2.1: Arquitectura de um driver JDBC do tipo 4 para SQL Server 2008.
2.1
Camada TDS
A camada TDS do driver JDBC constitui a implementação do protocolo Tabular Data
Stream, que consiste no protocolo de transporte de informação entre uma aplicação cliente e
o SQL Server.
Esta secção apenas descreve a interface que esta camada disponibiliza. No apêndice B é
fornecida uma resumida descrição do protocolo. Informação mais detalhada sobre o protocolo
pode ser obtida nas páginas MSDN dedicadas ao TDS [11] ou na especificação da Sybase [63].
A camada TDS fornece duas entidades que permitem à camada superior interagir com o
SQL Server sem conhecer as especificidades do protocolo: TDSMessage e ITDSResultSet.
2.1.1
TDSMessage
A classe TDSMessage implementa toda a comunicação com o servidor. A comunicação é
realizada utilizando um socket TCP.
Os serviços disponibilizados pela classe TDSMessage consistem em invocações de serviços
do SQL Server. A classe converte os pedidos da camada superior na linguagem do SQL
Server. A Figura 2.2 apresenta os serviços disponibilizados pela classe.
14
Figura 2.2: Classe TDSMessage.
A classe UpdateValue que é utilizada nos métodos cursorUpdate, cursorUpdateAbsolute,
cursorDelete, cursorDeleteAbsolute e cursorInsert, tem como objectivo converter os valores
das colunas de uma linha no formato do protocolo. Converte o valor de um tipo de dados Java
num conjunto de bytes formatados de acordo com o TDS, o resultado é depois transferido
para o SQL Server na mensagem (actualização, remoção ou inserção).
A Tabela 2.1 apresenta uma sucinta descrição dos serviços da classe TDSMessage.
Tabela 2.1: Descrição dos serviços da classe TDSMessage
Nome
close
login
executeSQLBatch
cursorClose
cursorOpen
cursorUpdate
cursorUpdateAbsolute
cursorDelete
cursorDeleteAbsolute
cursorInsert
cursorFetch
cursorRefresh
cursorRows
Descrição
Fecha os streams de comunicação com o servidor.
Envia um pedido de login ao servidor.
Envia um pedido de execução de um batch de comandos SQL ao
servidor.
Envia um pedido ao servidor para fechar e desalocar um cursor.
Envia um pedido ao servidor para alocar e abrir um cursor.
Envia um pedido ao servidor de actualização de valores das colunas de uma linha, considerando uma posição relativa.
Envia um pedido ao servidor de actualização de valores das colunas de uma linha, considerando uma posição absoluta.
Envia um pedido ao servidor de remoção de uma linha, considerando uma posição relativa.
Envia um pedido ao servidor de remoção de uma linha, considerando uma posição absoluta.
Envia um pedido ao servidor de inserção de uma linha.
Envia um pedido ao servidor de carregamento de linhas do result
set.
Envia um pedido ao servidor de recarregamento de linhas do result
set.
Envia um pedido ao servidor de informação relativamente ao
número total de linhas do result set do cursor.
15
2.1.2
ITDSResultSet
Quando o servidor envia ao cliente um result set1 , este vem formatado de acordo com o
protocolo. A interface ITDSResultSet fornece serviços para extrair a informação do result
set, garantindo acesso aos dados sem a necessidade do conhecimento dos detalhes.
A Figura 2.3 apresenta os serviços fornecidos pela interface ITDSResultSet.
Figura 2.3: Interface ITDSResultSet.
A descrição dos serviços da interface ITDSResultSet é apresentada na Tabela 2.2.
Tabela 2.2: Descrição dos serviços da interface ITDSResultSet
Nome
getCursor
columnCount
rowCount
isEmpty
getXXX
wasNull
insertRow
updateRow
getColumnName
getUpdateRowCount
1
Descrição
Obtém o identificador do cursor do servidor.
Obtém a quantidade de colunas que cada linha do result set possui.
Obtém a quantidade de linhas que foram enviadas para o cliente.
Verifica se a quantidade de linhas é zero.
Obtém o valor de uma coluna, dado o seu ı́ndice. Existe um
método get para cada um dos tipos de dados Java suportados:
int, double, String e Date.
Verifica se o valor da coluna lido é SQL NULL.
Insere uma linha na cópia do result set que existe no cliente.
Actualiza uma linha da cópia do result set que existe no cliente.
Obtém o nome de uma coluna, dado o seu ı́ndice.
Obtém o número de linhas que foram alteradas na base de dados
como resultado da execução de um comando DML.
Resultado da execução de uma statement SQL
16
2.2
Camada JDBC
Esta camada é constituı́da por um conjunto de classes que implementam as interfaces
definidas na API JDBC.
Neste trabalho procedeu-se apenas a uma implementação parcelar da API. Um dos objectivos principais para o driver JDBC era suportar a execução de statements SQL e obter
acesso a um result set. A Tabela 2.3 lista as interfaces que foram implementadas e identifica
as classes que as implementam. As interfaces implementadas pertencem todas ao pacote
java.sql.
No capı́tulo 3 é apresentada uma explicação mais detalhada da implementação da interface
ResultSet.
Tabela 2.3: Interfaces da API JDBC implementadas.
Interface
Driver
Classe
CDriver
Statement
CStatement
ResultSet
CResultSet
Descrição
Esta interface tem que ser obrigatoriamente implementada por um
driver. A classe java.sql.DriverManager encarrega-se de carregar os
drivers, que mais tarde serão usados para criar uma ligação à base de
dados.
Esta interface permite executar statements SQL e obter o respectivo
result set.
Esta interface permite obter acesso ao resultado da execução de uma
statement SQL. Existem serviços para leitura e modificação do result
set.
17
18
Capı́tulo 3
Arquitectura do ResultSet
Concorrente
3.1
Introdução
O ResultSet é uma interface que fornece uma abstracção ao paradigma Orientado a Objectos para um conceito do paradigma Relacional: o cursor. O comportamento e terminologia
envolvidos na criação do objecto result set, a sua manipulação, até à sua remoção são os mesmos que se encontram associados a um cursor do servidor (também conhecido como cursor
de base de dados).
A implementação da interface ResultSet cria um cursor no SQL Server1 , e na sua operação
interage com o cursor do servidor de modo a obter ou actualizar os dados do dataset 2 associado
ao cursor. Este dataset corresponde ao conjunto de linhas que satisfazem a condição da
statement SQL executada usando o método executeQuery da interface Statement. A relação
entre estas entidades encontra-se representada na Figura 3.1.
Figura 3.1: Representação da relação entre o ResultSet, o cursor servidor e o dataset
1
Normalmente é feita uma para excepção para o ResultSet do tipo FORWARD ONLY/READ ONLY em
que o ResultSet é obtido através da execução de um batch.
2
Os termos dataset e result set são equivalentes quando se referem aos dados seleccionados por uma statement SQL. O termo result set também é usado no documento para se referir a uma instância da interface
ResultSet.
19
Devido a esta relação ResultSet/cursor podemos dizer que criar uma instância do ResultSet
corresponde a criar um cursor cliente.
3.1.1
Problema
A relação de 1 para 1 entre ResultSet e cursor, implica que para cada ResultSet criado
num programa Java, irá ser alocado e aberto um novo cursor no servidor. Como a API JDBC
não define um mecanismo que permita tirar partido de um ambiente multihreaded, para se
poder trabalhar concorrentemente sobre um dataset é necessário criar um ResultSet/cursor
para cada entidade concorrente, o que desde logo implica uma maior alocação de recursos.
A maior alocação de recursos traduz-se em desperdı́cio, porque está claro que vai haver
replicação várias vezes da mesma informação. E este é um problema que se verifica tanto no
lado do cliente como do servidor. Do lado do cliente existe a instanciação de mais objectos.
Do lado do servidor existem mais cursores alocados e abertos.
3.1.2
Ideia base da solução
As boas práticas do acesso a informação em base de dados ensinam que se deve evitar
a utilização de cursores pois normalmente eles utilizam muitos recursos e reduzem o desempenho e a escalabilidade das aplicações [71], e que se devem utilizar alternativas, como por
exemplo:
• Ciclos while
• Tabelas temporárias
• Tabelas derivadas
• Múltiplas queries
Uma vez que o modo de operação de um ResultSet é linha a linha, temos de continuar
a utilizar cursores do servidor. Mas o que se pode fazer é diminuir o número de cursores
utilizados. Por isso a ideia base consiste em transformar a relação de 1 para 1 numa relação
de muitos para um, isto é, permitir que várias instâncias do ResultSet utilizem o mesmo
cursor do servidor. Esta alteração é assinalada na Figura 3.2.
Figura 3.2: Relação de muitos para um entre o ResultSet e o cursor do servidor.
20
A implementação encontra-se, deste modo, preparada para operar num ambiente concorrente, garantindo uma cooperação correcta entre as entidades concorrentes. A cada entidade
é atribuı́da uma referência para uma instância do ResultSet que por sua vez opera concorrentemente com as outras instâncias sobre o mesmo cursor, permitindo o desejado acesso
concorrente ao dataset.
Agora que já se sabe qual é a ideia base da solução, nas duas próximas secções serão
descritas duas possı́veis soluções concretas; uma implementando a concorrência ao alto nı́vel
e outra ao baixo nı́vel.
3.2
ResultSet Wrapper
Esta primeira solução é independente da implementação do driver, isto é, funciona para
qualquer driver que existe. A ideia base consiste em criar um mecanismo que controle os
acessos concorrentes a um objecto result set partilhado, e providencie uma implementação dos
serviços garantindo o acesso correcto ao result set, resolvendo assim os problemas identificados
anteriormente (secção 1.5).
O acesso correcto ao result set implica que para cada entidade que lhe acede seja guardado
o número da linha do result set em que ele se encontra a trabalhar, e quando voltar a ser a
entidade activa o result set volta a encontrar-se nessa linha. Para tal é necessário que a linha
do result set em que cada entidade se encontra a trabalhar seja guardada quando existe troca
de entidade activa, e seja restaurada a linha quando voltar a ser a entidade activa. A operação
que guarda o número da linha do result set denomina-se por salvaguarda de contexto e
a operação que move o result set para a linha guardada denomina-se por restauração de
contexto.
A Figura 3.3 apresenta o diagrama de classes da solução ResultSet Wrapper.
Figura 3.3: Diagrama de classes da solução ResultSet Wrapper.
A solução consiste em criar cursores cliente que encapsulam (wrap) e partilham a mesma
instância de um ResultSet. A cada thread que pretende trabalhar sobre o result set é lhe
atribuı́do um cursor cliente. O processo de salvaguarda/restauro de contexto é realizado pelo
21
cursor cliente, e é totalmente transparente para o utilizador. Isto significa que o modo de
utilização desta solução é rigorosamente igual à utilização da interface ResultSet.
Existem quatro tipos de cursores:
CursorForwardReadOnly
Wrapper para um result set FORWARD ONLY/READ ONLY.
CursorForwardUpdate
Wrapper para um result set FORWARD ONLY/UPDATABLE.
CursorScrollReadOnly
Wrapper para um result set SCROLLABLE SENSITIVE/READ ONLY.
CursorScrollUpdate
Wrapper para um result set SCROLLABLE SENSITIVE/UPDATABLE.
Cada tipo de cursor implementa os métodos da interface ResultSet que fazem sentido,
lançando uma excepção dizendo que a operação não é suportada no caso contrário. Por
exemplo, o CursorScrollReadOnly implementa os métodos relacionados com o movimento
do cursor, e lança excepção se se tentar invocar um método de update, enquanto que o
CursorForwardUpdate suporta os métodos de update e lança uma excepção se se tentar
mover o cursor para uma linha especı́fica (método absolute).
A gestão dos contextos dos cursores cliente é realizada pelos próprios cursores. Cada
um tem a noção da linha do result set onde se encontra acedendo à variável currentRow
da própria classe. Assim, quando é o cursor cliente activo, se for necessário recupera o seu
contexto, movendo o result set para a linha onde se encontrava a trabalhar. A detecção
da mudança do cursor activo é realizada com o auxı́lio da variável currentCursor que tem
visibilidade ao nı́vel da classe AbstractCursor e que guarda o identificador do cursor activo.
Quando um cursor entra em execução, o primeiro procedimento é verificar se o valor de
currentCursor é igual ao seu identificador (id), se for igual então não é necessário mover o
result set, porque ele foi o último cursor a trabalhar com o result set, caso contrário invoca o
método do result set que o posiciona na linha onde se encontrava a trabalhar anteriormente.
O trabalho sobre o result set tem que ser realizado num regime de acesso exclusivo, para o
obter esse acesso exclusivo (lock) é utilizada a variável lock que é uma instância da classe
ReentrantLock. Esta classe providencia o mesmo comportamento e semântica que o monitor
explı́cito acedido usando métodos e blocos synchronized, mas com funcionalidades extra
[50].
3.2.1
Utilização
A criação de um result set wrapper é realizada por intermédio da interface ICursorFactory. Esta interface fornece um serviço para construir cada um dos tipos de cursor (CursorForwardReadOnly, CursorForwardUpdate, CursorScrollReadOnly e CursorScrollUpdate). A
interface da fábrica de cursores é implementada pela classe CursorFactoryImpl, que recebe
22
no seu construtor o objecto result set que será mais tarde partilhado pelos cursores. No acto
de construção de um cursor é verificado se o result set encapsulado é compatı́vel com o tipo
de cursor que se está a pedir, lançando-se uma excepção numa situação de incompatibilidade.
Por exemplo, se o método createForwardRead é invocado e o result set é do tipo SCROLLABLE SENSITIVE/READ ONLY uma excepção a explicar a situação é criada e lançada.
A Figura 3.4 apresenta o diagrama de classes com a fábrica de cursores.
Figura 3.4: Diagrama das classes envolvidas na utilização das solução Cursor Wrapper.
Para utilizar esta solução primeiro cria-se um objecto ResultSet, como se criaria habitualmente utilizando o método executeQuery da interface Statement. Em seguida cria-se uma
instância da fábrica de cursores passando como argumento o result set ao seu construtor. O
último passo consiste em utilizar um dos serviços disponibilizados pela interface da fábrica
para criar o wrapper do result set desejado.
A Listagem 3.1 exemplifica a utilização da solução ResultSet Wrapper para um result set
do tipo TYPE FORWARD ONLY/CONCUR UPDATABLE.
Listagem 3.1: Criação de um ResultSet Wrapper.
1
3
5
// Criação do result set.
ResultSet rs = stmt . executeQuery ( sql );
// Criação da fábrica de cursores.
ICursorFactory factory = new CursorFactoryImpl ( rs );
// Criação do wrapper (cursor).
ResultSet cursor = factory . createForwardUpdate ();
O objecto stmt é uma instância de Statement e foi criado com o tipo adequado ao tipo
de wrapper criado na listagem. O argumento sql é uma String que contém a statement SQL
que dá origem ao result set.
3.3
Cursor Concorrente
O ResultSet Wrapper parece ser uma solução válida, no entanto, ao colocar o controlo
de acesso à região crı́tica num nı́vel tão alto, diminui-se a concorrência porque existe mais
código que poderia ser executado em paralelo e que passa a ser executado sequencialmente.
Como consequência perde-se a oportunidade de explorar sistemas com multiprocessador e
diminui-se também o throughput[61, 73].
23
Usando um driver JDBC existente implica que o melhor que se consegue é mesmo ficar
nesse nı́vel alto de concorrência. Mas implementando um driver próprio tem-se acesso ao funcionamento interno, e por isso consegue-se analisar e descobrir os pontos que são totalmente
concorrentes (isto é, que podem ser executados em paralelo), e também descobrir os pontos
em que se acede a recursos partilhados e tem que haver por isso acesso sequencial e exclusivo. Nesta secção é apresentada uma solução que começou pela implementação de um driver
JDBC do tipo 4 (Native-Protocol Driver). A seguir à implementação do driver procedeu-se
à inclusão do mecanismo apresentado na secção 3.1.2 na implementação do driver.
3.3.1
Anatomia de um ResultSet
Nesta secção pretende-se, sem entrar em grandes detalhes, dar a entender a estrutura
geral de uma implementação do ResultSet, fazendo assim a cobertura de alguns conceitos e
terminologias que ajudam a perceber as subsequentes secções.
A classe CResultSet implementa a interface ResultSet, comunicando com camada que
implementa o protocolo Tabular Data Stream (TDS) para executar as operações sobre o
result set. A Figura 3.5 apresenta o diagrama de classes simplificado da implementação do
ResultSet.
Figura 3.5: Visão geral da implementação do ResultSet
A Tabela 3.1 possui uma descrição dos atributos da classe CResultSet.
Tabela 3.1: Descrição dos atributos da implementação do ResultSet.
Atributo
Descrição
cursor
Identificador do cursor do servidor. É obtido na resposta do servidor ao pedido de criação do cursor (ver secção C.2).
currentRow
Número da linha actual do cursor cliente.
rsIndex
Índice para aceder ao cache.
tdsResultSet
Cache do result set.
Apesar de o ResultSet operar linha a linha interagindo com o cursor do servidor, cada
instância de uma implementação do ResultSet possui em memória um conjunto de linhas,
isto é, possui um cache, de modo a diminuir o número de mensagens trocadas entre o cliente
e o servidor. Tal como na arquitectura de computadores em que os processadores usam o
24
princı́pio da localidade de referência[76, 72] e transferem para a memória de mais alto nı́vel
não só os dados pedidos, mas um bloco de dados adjacentes; no acesso a uma linha de um
result set existe uma grande probabilidade de uma linha próxima ser também utilizada (pelo
princı́pio), é por isso uma boa estratégia, sempre que existe o pedido de uma linha que não
se encontra na cache, transferir um bloco de linhas adjacentes.
A classe CResultSet possui um objecto que implementa a interface ITDSResultSet. Esse
objecto corresponde ao cache referido anteriormente e possui o result set formatado de acordo
com o protocolo TDS. O cache essencialmente é constituı́do por uma lista com as linhas do
result set e possui ainda a descrição das colunas que formam uma linha, nessa descrição está
presente, por exemplo, o tipo de dados da coluna e o respectivo nome.
O cache é indexado usando a variável rsIndex, cuja gama de valores vai desde zero até
ao tamanho da cache. O tamanho da cache é definido pelo método setFetchSize da interface
ResultSet. A variável rsIndex para além de ser utilizada para ler o conteúdo de uma linha
do result set, pode também ser utilizada nas operações de actualização e remoção (se o tipo
de result set as suportar). O cursor servidor actualiza o result set utilizando um ı́ndice
relativo ao seu buffer (ver apêndice C, particularmente a secção C.1), esse ı́ndice corresponde
exactamente ao valor de rsIndex. A operação de inserção ignora este valor, porque as inserções
são realizadas após a última linha do result set.
O número da linha actual do result set é guardado pela variável currentRow, cujo valor
varia na gama 1 até ao número total de linhas seleccionadas pela execução da statement
SQL. O seu valor pode ser obtido a partir do método getRow da interface ResultSet, e
tem particular importância para o funcionamento interno de CResultSet na verificação da
necessidade de efectuar um fetch de um novo conjunto de linhas. Se o valor de currentRow
não estiver contido na gama de linhas que o cache possui, então é preciso pedir novas linhas
ao cursor do servidor.
3.3.2
Cache individual
Uma vez que diminuindo a disputa pelo acesso a uma memória partilhada melhora-se
a eficiência de uma aplicação [61], a ideia desta solução é tentar diminuir ao máximo os
pontos em que é necessário proceder ao lock para a obter acesso exclusivo. Daı́ surgiu a
implementação de um ResultSet que para cada instância criasse uma cópia (normalmente
parcial) do result set. O que isto significa é que cada cursor cliente possui cache próprio.
O cache individual possui as seguintes vantagens:
1. O acesso ao cache, tanto para leitura como para escrita, é realizado sem restrições, isto
é, não há a necessidade obter lock sobre o objecto do cache.
2. Numa situação de cópia parcial do result set diminui-se o número de vezes que se
interage com o servidor.
O segundo ponto carece de uma explicação. Imaginemos um cache com capacidade de
uma linha, partilhado por alguns threads. Um thread T1 está a trabalhar na linha 5 do
25
result set, entretanto T1 deixa de ser o thread em execução, que passa a ser o thread T2 cuja
linha do result set é a 10 e que agora terá de ser pedida ao servidor. A troca de thread em
execução provoca uma grande quantidade de mensagens trocadas entre o cliente e o servidor,
pois cada thread precisa de pedir as linhas em que está a trabalhar. E apesar de se ter
utilizado um exemplo simples em que o cache tem capacidade de apenas uma linha, caches
com capacidades maiores sofrem ainda mais de perda de desempenho (a demonstração e
explicação deste fenómeno encontram-se nas secções 5.4 e 6.1.4, respectivamente). Com um
cache individual o carregamento do conteúdo do cache é realizado consoante as necessidades
individuais do cursor cliente, pelo que se resolve o problema do maior volume de mensagens
trocadas entre o cliente e o servidor, porque o cursor cliente tem em cache as linhas em que
estava a trabalhar.
O cache individual possui as seguintes desvantagens:
1. Replicação da informação; podem existir caches cujo conteúdo seja o mesmo.
2. A actualização de um dos caches não reflete as alterações nos restantes caches, sendo
necessário os cursores pedirem ao servidor os novos dados.
A Figura 3.6 apresenta o diagrama de classes da solução com cache individual. Note-se
a relação de 1 para 1 entre CursorIndividualCache e ITDSResultSet, que determina o cache
como único para cada cursor cliente.
Figura 3.6: Diagrama de classes do cursor com cache individual.
As classes CursorForwardOnlyReadOnly, CursorForwardOnlyUpdatable, CursorScrollableReadOnly e CursorScrollableUpdatable implementam um result set do tipo FORWARDONLY/READ ONLY,
FORWARD ONLY/UPDATABLE,
SCROLLABLE SENSITIVE
/READ ONLY e SCROLLABLE UPDATABLE, respectivamente. Estas classes reimplementam os métodos que o respectivo result set não suporta, indicando um erro se esses métodos
forem invocados.
A classe CursorIndividualCache tem como função adequar a implementação do ResultSet
realizada pelo CResultSet à situação de ter um cache individual. A Tabela 3.2 descreve os
métodos que têm de ser reimplementados (override) pela classe CursorIndividualCache.
26
Tabela 3.2: Métodos reimplementados pela classe CursorIndividualCache.
Método
next
update
delete
3.3.3
Explicação
Cada cursor cliente pode mover o cursor do servidor, assim quando um cursor cliente
pede ao servidor que mova o seu cursor para o próxima linha ou próximo buffer, a
posição inicial pode não ser a correcta para o cursor cliente que faz o pedido. Por isso,
o método next é reimplementado para executar um FETCH ABSOLUTE em vez de
um FETCH NEXT (a secção A.1.1 apresenta uma explicação destes termos).
Aqui também existe o problema do cursor do servidor poder estar noutra
linha, por isso o RPC sp cursor tem que ser invocado com um optype igual a
ABSOLUTE|UPDATE (conjunção de duas opções usando o operador OR) em vez
de UPDATE (ver secção C.1). Ao efectuar esta alteração do valor de optype o cursor
do servidor passa a considerar o argumento rownum como sendo o número da linha
contando desde o inı́cio do result set em vez de contar desde o inı́cio do buffer.
A situação é semelhante ao update, mas o valor DELETE do argumento optype é
substituı́do por ABSOLUTE|DELETE.
Cache partilhado
Como foi visto na secção anterior o cursor com cache individual será vantajoso numa
situação em os threads trabalham sobre gamas de linhas dı́spares e distantes, no entanto
provoca a replicação de informação transmitida e guardada no cliente.
Esta secção apresenta a implementação do cursor cliente, em que as várias instâncias do
cursor partilham o mesmo cache.
O cache partilhado tem as seguintes vantagens:
1. Só existe uma cópia dos dados do result set.
2. A actualização do cache realizada por um cursor cliente é visı́vel pelos restantes cursores
cliente.
O cache partilhado tem as seguintes desvantagens:
1. Todo o acesso ao cache, seja para leitura ou escrita, tem que ser precedido de um lock
no objecto que representa o cache.
2. O número de mensagens trocadas com o servidor pode aumentar, diminuindo o desempenho da aplicação, principalmente numa situação em que os cursores cliente trabalhem
em gamas de linhas distantes entre si.
Se o cache for partilhado nas operações de leitura (getInt, getDate, ...) é necessário
recuperar o contexto, isto é, verificar se a linha para a qual se moveu o cursor cliente ainda está
presente no cache, e efectuar um fetch caso não esteja em cache. Num ambiente multithreaded
é muito frequente outro thread entrar em execução e alterar as linhas que estão presentes
no cache. Isto provoca uma maior troca de mensagens entre o cliente e o servidor porque
é necessário alterar várias vezes o conteúdo do cache. Tentando resolver este problema, na
27
implementação do cursor com cache partilhado são seleccionadas e guardadas em cache todas
as linhas do dataset. Isto transforma o cache hit ratio em 100%.
A Figura 3.7 apresenta o diagrama de classes da solução com cache partilhado. Podemos
ver que a relação entre os cursores cliente e o cache é de muitos para 1; a mesma memória
cache (ITDSResultSet) é utilizada por vários cursores cliente.
Figura 3.7: Diagrama de classes da implementação do cursor com cache partilhado.
Os atributos e os métodos de CursorSharedCache não são apresentados, porque essencialmente o que a classe faz é reimplementar (override) os métodos da interface ResultSet,
adequando-os ao facto de agora estar a operar com um cache partilhado. A Tabela 3.3
apresenta os métodos que têm de ser reimplementados pela classe CursorSharedCache.
Tabela 3.3: Métodos reimplementados pela classe CursorSharedCache
Método
afterLast
beforeFirst
getInt
getString
getDouble
getDate
updateRow
insertRow
scroll
Explicação
A implementação normal deste método invoca o RPC sp cursorfetch com o
optype FETCH AFTER (A.1.1), o que faz com que o cursor do servidor seja
movido para uma posição a seguir à última linha e o buffer fique vazio. Com
cache partilhado não há a necessidade de mover o cursor do servidor, apenas o
cursor cliente. Também não é desejável que o cache seja esvaziado.
A situação é semelhante ao afterLast, mas o optype é FETCH BEFORE
(A.1.1), e o cursor do servidor é movido para uma posição anterior à primeira
linha do result set.
O acesso ao cache para leitura só pode ocorrer depois da obtenção do lock.
O acesso ao cache para leitura só pode ocorrer depois da obtenção do lock.
O acesso ao cache para leitura só pode ocorrer depois da obtenção do lock.
O acesso ao cache para leitura só pode ocorrer depois da obtenção do lock.
O acesso ao cache para actualizar uma linha só pode ocorrer depois da obtenção
do lock.
O acesso ao cache para inserir uma linha só pode ocorrer depois da obtenção
do lock.
O método scroll serve de boilerplate para os métodos de navegação no result
set. Como no cache partilhado todas as linhas do result set já se encontram no
cache, não há a necessidade de verificar se uma linha se encontra em cache e
em caso negativo pedi-la ao servidor.
28
A partilha do cache implica a utilização de um mecanismo de sincronização. O cursor
cliente com cache partilhado não só tem de garantir acesso exclusivo ao canal de comunicação
com o servidor, como tem também de garantir o acesso exclusivo ao cache. Este aspecto
parece constituir uma desvantagem em relação à implementação com cache individual, sendo
um potencial factor de perda desempenho. No entanto, com o cache partilhado não existe o
overhead da construção de vários caches. Esta discussão ficará para a secção de análise dos
resultados (secção 6.1) apresentados no capı́tulo 5.
As classes CursorForwardOnlyReadOnly, CursorForwardOnlyUpdatable, CursorScrollableReadOnly e CursorScrollableUpdatable implementam um result set do tipo FORWARDONLY/READ ONLY,
FORWARD ONLY/UPDATABLE,
SCROLLABLE SENSITIVE
/READ ONLY e SCROLLABLE UPDATABLE, respectivamente. Estas classes reimplementam os métodos que o respectivo result set não suporta, indicando um erro se esses métodos
forem invocados.
3.3.4
Utilização
O driver implementado é utilizado tal como outro driver JDBC. A versão 4.0 da API
JDBC introduziu a funcionalidade de carregamento de um java.sql.Driver [46], através
da utilização do mecanismo Java SE Service Provider. Isto significa que já não é necessário
invocar Class.forName. O driver suporta esta funcionalidade, por isso para obter uma
ligação à base de dados basta utilizar o código apresentado na Listagem 3.2. O jar do driver
tem que se encontrar no classpath.
Listagem 3.2: Obtenção de uma ligação.
2
String url = ...
Connection con = DriverManager . getConnection ( url );
Em que url é uma String no formato descrito na secção 1.4.2.
O que é novo é a possibilidade de criar vários objectos result set (cursores cliente) que
utilizam o mesmo cursor do servidor, operando assim sobre o mesmo dataset. Para tal é
necessário obter uma instância da interface ICStatement. Esta interface fornece o serviço
getCursor() que devolve um cursor cliente. A interface ICStatement estende a interface
Statement. A classe que as implementa devolve um cursor com cache partilhado (3.3.3)
quando o valor de fetch size é igual a zero, e devolve um cursor com cache individual (3.3.2)
quando o valor de fetch size é superior a zero (um valor inferior a zero é inválido). Uma
vez que por pré-definição o valor de fetch size é superior a zero, se o método setFetchSize da
interface ResultSet não for explicitamente invocado então será criado um cursor com cache
individual. Será então boa prática utilizar o método setFetchSize explicitamente, para evitar
confusões.
29
Figura 3.8: Classes principais na utilização da solução Cursor.
As listagens 3.3 e 3.4 exemplificam a criação de um cursor cliente com cache partilhado e
de um cursor cliente com cache individual, respectivamente.
Listagem 3.3: Criação de um cursor cliente com cache partilhado.
4
stmt = con . createStatement ();
// Com fetchSize igual a zero todas as linhas do result set irão ser colocadas em cache.
stmt . setFetchSize (0);
stmt . executeQuery ( sqlQuery );
6
ResultSet cursor = (( ICStatement ) stmt ). getCursor ();
2
Listagem 3.4: Criação de um cursor cliente com cache individual.
1
3
stmt = con . createStatement ();
// Cache individual com capacidade igual a 25 linhas.
stmt . setFetchSize (25);
stmt . executeQuery ( sqlQuery );
5
ResultSet cursor = (( ICStatement ) stmt ). getCursor ();
Com a excepção da última linha de cada listagem, a criação é igual para ambos e deve
ser bastante familiar e intuitiva para quem já utiliza a API JDBC. A partir da criação, cada
cursor cliente pode ser utilizado como um ResultSet, tal como está definido na documentação
da API [47].
30
Capı́tulo 4
Benchmark
Uma vez implementadas as soluções, é necessário realizar uma avaliação do seu desempenho. Para tal foi criado um Domain-Specific Benchmark [59], que para ser útil tem que
ser:
Relevante: Tem que medir o desempenho máximo que um sistema apresenta durante uma
operação tı́pica.
Portável: Deve ser fácil de realizar em diferentes sistemas e arquitecturas.
Escalável: Deve ser aplicável a pequenos e a grandes sistemas.
Simples: Tem que ser facilmente compreensı́vel.
Foi com estas caracterı́sticas em mente que o benchmark foi construı́do.
4.1
Benchmark principal
O benchmark consiste em criar um determinado número de threads e medir o tempo
total que eles levam a executar a sua tarefa. A tarefa é atribuı́da no momento da criação dos
threads e corresponde à execução de um contexto.
Foram realizados testes nos contextos: leitura, actualização, inserção e remoção. O
código executado para cada contexto encontra-se nas listagens 4.1 até 4.4.
Estes contextos foram testados num cenário que tenta representar uma situação de utilização de um result set por vários threads. O cenário consiste em dividir logicamente um
result set e atribuir uma gama de linhas a cada thread. Na sua execução, cada thread realiza
a sua tarefa sobre as linhas lhe são destinadas. De notar que este é um cenário de vários
possı́veis.
Listagem 4.1: Contexto de leitura.
1
int firstLine = counter . getAndAdd ( linesPerThread );
int lastLine = firstLine + linesPerThread - 1;
3
31
5
7
9
11
13
15
rs . absolute ( firstLine - 1);
for ( int i = firstLine ; i <= lastLine ; ++ i ) {
rs . next ();
val1 = rs . getInt (1);
val2 = rs . getString (2);
val3 = rs . getString (3);
val4 = rs . getDate (4);
val5 = rs . getDouble (5);
val6 = rs . getInt (6);
val7 = rs . getDate (7);
val8 = rs . getString (8);
}
Listagem 4.2: Contexto de actualização.
2
initUpdateValues ();
int firstLine = counter . getAndAdd ( linesPerThread );
int lastLine = firstLine + linesPerThread - 1;
4
6
8
10
12
14
16
rs . absolute ( firstLine - 1);
for ( int i = firstLine ; i <= lastLine ; ++ i ) {
rs . next ();
rs . updateInt (1 , val1 );
rs . updateString (2 , val2 );
rs . updateString (3 , val3 );
rs . updateDate (4 , val4 );
rs . updateDouble (5 , val5 );
rs . updateInt (6 , val6 );
rs . updateDate (7 , val7 );
rs . updateString (8 , val8 );
rs . updateRow ();
}
Listagem 4.3: Contexto de inserção.
1
3
5
7
9
11
13
initUpdateValues ();
for ( int i = 0; i < linesPerThread ; ++ i ) {
rs . moveToInsertRow ();
rs . updateInt (1 , val1 );
rs . updateString (2 , val2 );
rs . updateString (3 , val3 );
rs . updateDate (4 , val4 );
rs . updateDouble (5 , val5 );
rs . updateInt (6 , val6 );
rs . updateDate (7 , val7 );
rs . updateString (8 , val8 );
rs . insertRow ();
rs . moveToCurrentRow ();
}
32
Listagem 4.4: Contexto de remoção.
1
int firstLine = counter . getAndAdd ( linesPerThread );
int lastLine = firstLine + linesPerThread - 1;
3
5
7
rs . absolute ( firstLine - 1);
for ( int i = firstLine ; i <= lastLine ; ++ i ) {
rs . next ();
rs . deleteRow ();
}
A variável inteira linesPerThread possui o número de linhas atribuı́das a cada thread, e
o seu valor é atribuı́do directamente como parâmetro de teste. O número de linhas da tabela
é igual ao número de linhas por thread multiplicado pelo número de threads. A variável
counter é uma instância da classe AtomicInteger [49], e é-lhe atribuı́do o valor 1 no inı́cio
de cada teste. A variável counter auxilia o processo de divisão do result set em gamas. Cada
thread obtém o valor da variável, que corresponde ao número da primeira linha da gama, e
calcula o valor do inı́cio da próxima gama. O valor do número da última linha da gama é
calculado adicionando o número de linhas por thread ao valor da primeira linha da gama.
Foram realizados benchmarks com o driver da Microsoft (disponı́vel em [10]), com a
solução ResulSet Wrapper e a com solução Cursor.
De modo a apresentar e discutir os resultados de modo mais sintético, utilizaram-se os
seguintes nomes para identificar cada solução:
• M SJDBC: Teste usando o driver da Microsoft;
• CJDBCI : Teste usando o driver implementado, na versão com cache individual;
• CJDBCS : Teste usando o driver implementado, na versão com cache partilhado;
• W JDBC: Teste usando o driver da Microsoft, na versão Wrapper ;
O benchmark do M SJDBC cria um result set para cada thread, porque o result set
não pode ser correctamente partilhado pelas entidades concorrentes. Para o benchmark do
W JDBC é criado um result set do driver da Microsoft que depois é partilhado entre os
threads, e cada thread acede ao result set através de um wrapper. Para os benchmarks do
CJDBCI e CJDBCS é criado um cursor cliente do result set para cada thread.
O tipo de result set dos testes será sempre TYPE SCROLLABLE SENSITIVE. Uma vez
que um cursor forward-only só permite percorrer o dataset numa direcção, não é possı́vel
recuperar o contexto, nem existe a possibilidade de atribuir uma porção do dataset a cada
thread, pois este não pode mover o result set para uma linha especı́fica (a operação absolute
não está disponı́vel). A solução ResultSet Wrapper tem como pedra basilar o restauro do
contexto, e como já foi dito não é possı́vel restaurar o contexto a não ser que o result
set seja scrollable; por estes motivos não são realizados testes com o tipo de result set
TYPE FORWARD ONLY. No contexto da Leitura o tipo de concorrência do result set é
CONCUR READ ONLY e nos contextos da Actualização, Inserção e Remoção a concorrência
é do tipo CONCUR UPDATABLE.
33
Antes da execução propriamente dita do benchmark é necessário proceder à preparação
da base de dados, pois alguns testes de desempenho necessitam que existam dados na tabela
de testes.
Todas as statement SQL utilizadas nos testes de desempenho para criar os result set foram
realizadas sobre a tabela representada na Figura 4.1.
Figura 4.1: Tabela utilizada no benchmark.
As listagens 4.5 e 4.6 apresentam os passos que são efectuados na preparação da base de
dados dos testes de desempenho para os diferentes contextos.
Os contextos de Leitura, Actualização e Remoção limpam os dados da tabela, e introduzem novos dados antes de executar o benchmark. O contexto de Inserção só limpa os dados
da tabela antes de executar o benchmark, já que a própria execução consiste em introduzir
dados na tabela.
Listagem 4.5: Preparação para executar os contextos de Leitura, Actualização e Remoção.
1
3
Limpar a tabela de testes
Introduzir novos valores na tabela de testes
Executar o benchmark
Listagem 4.6: Preparação para executar o contexto de Inserção.
1
Limpar a tabela de testes
Executar o benchmark
Na execução do benchmark são medidos dois tempos: tempo de preparação (fig. 4.2),
que corresponde ao tempo necessário para executar as queries e criar os threads, e tempo de
execução (fig. 4.3), que corresponde ao tempo total que os threads criados levam a terminar
a sua tarefa.
Figura 4.2: Medição do tempo de preparação
34
Figura 4.3: Medição do tempo de execução
Na inicialização de variáveis são criados os result set apropriados a cada situação e
definidos parâmetros tais como o número de linhas por thread. Para o M SJDBC neste
passo não é criado algum result set, pois cada thread é que se encarrega de criar o seu
próprio result set. Para o W JDBC este passo também incluir criar a fábrica de cursores.
Na criação dos threads é atribuı́da uma tarefa (Runnable) a cada thread. A tarefa está
relacionada com o contexto. Os threads no M SJDBC criam agora o seu result set e os threads
das restantes soluções obtêm o cursor cliente para o result set criado no passo anterior.
Os resultados deste benchmark são apresentados nas secções 5.2 e 5.2.2, e discutidos nas
secções 6.1.1 e 6.1.2.
Para além do benchmark descrito até aqui, foram ainda criados dois benchmarks adicionais
que testam situações particulares, e que serão descritos nas secções seguintes.
4.2
Benchmark com atrasos
A motivação para realizar este benchmark é que o cenário do benchmark principal pode
ser considerado irrealista; normalmente existe algum processamento associado à leitura e
escrita de linhas, e admite-se que a introdução desse processamento possa vir a alterar os
resultados. Por isso resolveu-se introduzir a realização de uma tarefa entre operações de leitura/escrita, que provocará um atraso entre as invocações sucessivas de operações do ResultSet.
A introdução do atraso é realizada em dois locais: entre colunas e entre linhas; ver-se-á assim
se algum dos dois tem algum efeito nos resultados finais do benchmark. O resultados para
os dois locais foram obtidos em dois benchmarks separados.
Neste benchmark apenas o contexto de leitura e de actualização foram testados. O contexto de inserção do ponto de vista operacional é semelhante ao contexto de actualização, pelo
que os seus resultados seriam redundantes. O contexto de remoção não realiza processamento
dos valores das colunas, e é por isso excluı́do. As listagens 4.7, 4.8, 4.9 e 4.10 apresentam o
código executado por cada thread para os diferentes contextos.
Listagem 4.7: Contexto de leitura com atraso entre colunas.
2
4
6
int firstLine = counter . getAndAdd ( linesPerThread );
int lastLine = firstLine + linesPerThread - 1;
rs . absolute ( firstLine - 1);
for ( int i = firstLine ; i <= lastLine ; ++ i ) {
rs . next ();
val1 = rs . getInt (1);
35
ResultSetRunnable . delayfunc ();
val2 = rs . getString (2);
ResultSetRunnable . delayfunc ();
val3 = rs . getString (3);
ResultSetRunnable . delayfunc ();
val4 = rs . getDate (4);
ResultSetRunnable . delayfunc ();
val5 = rs . getDouble (5);
ResultSetRunnable . delayfunc ();
val6 = rs . getInt (6);
ResultSetRunnable . delayfunc ();
val7 = rs . getDate (7);
ResultSetRunnable . delayfunc ();
val8 = rs . getString (8);
ResultSetRunnable . delayfunc ();
8
10
12
14
16
18
20
22
}
Listagem 4.8: Contexto de leitura com atraso entre linhas.
1
3
5
7
9
11
13
int firstLine = counter . getAndAdd ( linesPerThread );
int lastLine = firstLine + linesPerThread - 1;
rs . absolute ( firstLine - 1);
for ( int i = firstLine ; i <= lastLine ; ++ i ) {
rs . next ();
val1 = rs . getInt (1);
val2 = rs . getString (2);
val3 = rs . getString (3);
val4 = rs . getDate (4);
val5 = rs . getDouble (5);
val6 = rs . getInt (6);
val7 = rs . getDate (7);
val8 = rs . getString (8);
ResultSetRunnable . delayfunc ();
15
}
Listagem 4.9: Contexto de actualização com atraso entre colunas.
1
3
5
7
9
11
13
initUpdateValues ();
int firstLine = counter . getAndAdd ( linesPerThread );
int lastLine = firstLine + linesPerThread - 1;
rs . absolute ( firstLine - 1);
for ( int i = firstLine ; i <= lastLine ; ++ i ) {
rs . next ();
ResultSetRunnable . delayfunc ();
rs . updateString (2 , val2 );
ResultSetRunnable . delayfunc ();
rs . updateString (3 , val3 );
ResultSetRunnable . delayfunc ();
rs . updateDate (4 , val4 );
ResultSetRunnable . delayfunc ();
36
rs . updateDouble (5 , val5 );
ResultSetRunnable . delayfunc ();
rs . updateInt (6 , val6 );
ResultSetRunnable . delayfunc ();
rs . updateDate (7 , val7 );
ResultSetRunnable . delayfunc ();
rs . updateString (8 , val8 );
ResultSetRunnable . delayfunc ();
rs . updateRow ();
15
17
19
21
23
}
Listagem 4.10: Contexto de actualização com a atraso entre linhas.
2
4
6
initUpdateValues ();
int firstLine = counter . getAndAdd ( linesPerThread );
int lastLine = firstLine + linesPerThread - 1;
rs . absolute ( firstLine - 1);
for ( int i = firstLine ; i <= lastLine ; ++ i ) {
rs . next ();
ResultSetRunnable . delayfunc ();
8
16
rs . updateString (2 , val2 );
rs . updateString (3 , val3 );
rs . updateDate (4 , val4 );
rs . updateDouble (5 , val5 );
rs . updateInt (6 , val6 );
rs . updateDate (7 , val7 );
rs . updateString (8 , val8 );
18
rs . updateRow ();
10
12
14
}
O código dos contextos deste benchmark é muito semelhante ao código do benchmark
principal, a diferença fundamental está na invocação do método delayfunc() da classe ResultSetRunnable entre cada operação sobre uma coluna para introduzir atraso entre colunas,
ou entre cada linha lida para introduzir atraso entre linhas. O código executado por este
método encontra-se na listagem 4.11. Realizando diversos testes, verificou-se que o método
introduz o atraso de aproximadamente delayms. A variável delayms pertence à classe ResultSetRunnable, e o seu valor pode ser alterado usando o método setDelay dessa mesma
classe.
Listagem 4.11: Método de atraso.
1
3
5
final static Object = new Object ();
public static void delayfunc () {
synchronized ( obj ) {
obj . wait ( delayms );
}
}
37
Os resultados deste benchmark são apresentados na secção 5.3, e discutidos na secção
6.1.3.
4.3
Cache individual vs Cache partilhado
Foi realizado um benchmark suplementar comparando a implementação do cursor cliente
com cache individual com a implementação do cursor cliente com cache partilhado. Este
benchmark tem o intuito de justificar a existência de ambos como solução ao problema, e
ainda justificar a decisão de utilizar um fetch size de 100% para o cache partilhado.
O benchmark contemplou apenas o contexto de leitura, pelo que o código executado pelos
threads no processo é o mesmo que foi apresentado na listagem 4.1. Decidiu-se assim porque
o contexto de leitura é o que mais directamente depende da implementação do cache, e por
isso é também o mais relevante para testar.
A ideia principal deste benchmark é verificar o efeito que vários valores de fetch size têm
em cada implementação do cache. Na medida em que a implementação do cache partilhado
por pré-definição carrega todas as linhas da tabela para o cache, isto é, tem um fetch size
de 100%, foi necessário realizar uma modificação à implementação, de modo a ser possı́vel
trabalhar com um cache partilhado que não escolhe todas as linhas; esta implementação
é denominada por CJDBCS M , a implementação do cache individual é denominada por
CJDBCI e a implementação normal do cache partilhado é denominada por CJDBCS .
O procedimento passou por calcular o valor de linhas para o fetch size aplicando uma
percentagem ao número total de linhas presentes na tabela. As várias percentagens definem
os contextos, e são as seguintes: fetch size 10%, fetch size 20%, fetch size 50%, fetch
size 75% e fetch size 100%. No entanto, nos resultados e nas discussões utiliza-se a notação
mais compacta: 10, 20, 50, 75 e 100 para designar os contextos.
O cenário de teste é o mesmo que foi utilizado no benchmark principal: a cada thread é
atribuı́da uma gama de linhas sobre a qual ele deve realizar a tarefa que lhe foi atribuı́da.
Os resultados deste benchmark são apresentados na secção 5.4, e discutidos na secção
6.1.4.
38
Capı́tulo 5
Resultados
Neste capı́tulo, primeiro são apresentadas as caracterı́sticas do sistema computacional
utilizado para realizar os benchmarks, e a seguir são apresentados os resultados para os
benchmarks descritos no capı́tulo 4.
Os gráficos apresentados refletem os resultados comparando as soluções aos pares efectuando um rácio. Isto permite perceber qual a solução que melhor responde à variação dos
parâmetros do benchmark. A variável independente é a quantidade de threads, a variável
dependente é o rácio do tempos obtidos para cada solução e cada série representa o número
de linhas que é atribuı́do a cada thread. Cada thread executou a tarefa associada a um
determinado contexto sobre esse número de linhas.
São apresentados os resultados obtidos para três tempos: execução (ExecT), preparação
(SetupT) e total (Total).
5.1
Plataforma de teste
Os resultados apresentados foram obtidos usando a seguinte plataforma:
• Cliente: Intel R CoreTM 2 Duo P8600 @ 2.40GHz, 4GB DDR2
• Servidor: Inter R PentiumTM SU4100 @ 1.30GHz, 4GB DDR3, Disco Rı́gido SATA
II 7200 RPM
• SQL Server 2008 versão 10.0.1600
• Java 1.6.0 17
• Microsoft SQL Server JDBC Driver 3.0 (sqljdbc4 - versão 3.0.1301.101, Abril de 2010)
Foram registadas 30 amostras para cada teste. Para dar maior valor estatı́stico ao resultados as 10 melhores amostras e as 10 piores foram ignoradas, e os resultados reflectem
portanto a média das restantes amostras. A razão para não ter em conta algumas amostras
deve-se ao facto que podem acontecerem condições favoráveis ou desfavoráveis, externas ao
39
ambiente de teste, que alteram o comportamento habitual. Assim, descartando estes resultados anómalos, obtém-se uma melhor estimativa. De notar que a opção por este processo
surge de observação empı́rica.
Entre o registo de cada amostra foi executado o garbage collector com o intuito de reciclar
os objectos anteriores que já não estejam a ser utilizados, libertando memória [56, 60].
Foi criada uma base de dados no SQL Server para registar os resultados e fornecer dados
para a execução dos cenários de teste, com um modelo de recuperação simples e com a opção
de estatı́sticas automáticas desactivada. Estas opções servem para diminuir o impacto que o
SQL Server possa ter nos resultados.
5.2
Benchmark principal
5.2.1
Comparação com MSJDBC
Esta secção apresenta os resultados do benchmark principal (secção 4.1). Os resultados
são apresentados sob a forma de uma comparação entre o desempenho registado para o
M SJDBC e as restantes soluções. A comparação é realizada através de um rácio dos tempos
registados.
5.2.1.1
Actualização
5.2.1.1.1
CJDBCI
A Figura 5.1 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCI , no contexto Actualização.
Figura 5.1: Comparação entre M SJDBC e CJDBCI , no contexto Actualização.
(b) SetupT
(a) ExecT
MSJDBC/CJDBCI
1.06
MSJDBC/CJDBCI
1.4
90
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.04
(c) Total
MSJDBC/CJDBCI
35 linhas/thread
65 linhas/thread
90 linhas/thread
80
1.02
70
1
60
0.98
50
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.35
1.3
1.25
1.2
0.96
40
1.15
0.94
30
1.1
0.92
20
1.05
0.9
10
0.88
10
30
50
quantidade de threads
70
90 0
1
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
Em relação ao tempo de execução, o CJDBCI apresenta um desempenho um pouco
inferior ao de JDBC, principalmente para os valores do número de linhas por thread maiores.
40
O aumento da quantidade de threads penaliza mais o desempenho da solução CJDBCI .
Com a ajuda do tempo de preparação, o CJDBCI consegue ser sempre mais rápido do
que JDBC no que se refere ao tempo total. Neste caso o aumento da quantidade de threads
beneficia o desempenho do CJDBCI , e para quantidades de threads baixas os resultados dos
valores do número de linhas por thread são aproximados, diferenciando-se um pouco para as
quantidades de threads mais altas, em que os valores de linhas mais altos penalizam mais o
desempenho do CJDBCI .
5.2.1.1.2
CJDBCS
A Figura 5.2 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCS , no contexto Actualização.
Figura 5.2: Comparação entre M SJDBC e CJDBCS , no contexto Actualização.
(b) SetupT
(a) ExecT
MSJDBC/CJDBCS
1.5
MSJDBC/CJDBCS
2
30
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.45
(c) Total
MSJDBC/CJDBCS
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.9
25
1.4
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.8
1.35
1.7
20
1.3
1.6
1.25
1.5
15
1.2
1.4
10
1.15
1.3
1.1
1.2
5
1.05
1.1
1
10
30
50
quantidade de threads
70
90 0
1
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
Aqui o desempenho do CJDBCS é sempre superior, conseguindo bons resultados totais,
pois para as quantidades de threads maiores consegue ser quase duas vezes melhor do que
JDBC.
Quanto maior é a quantidade de threads melhor é o desempenho de CJDBCS em relação
a JDBC.
Diferentes números de linhas por thread não implicam alterações significativas ao comportamento geral.
O tempo de preparação tem alguma importância, pois vem aumentar a superioridade do
CJDBCS em relação ao JDBC para os tempos totais.
5.2.1.1.3
W JDBC
A Figura 5.3 apresenta os resultados da comparação do desempenho de M SJDBC e de
W JDBC, no contexto Actualização.
41
Figura 5.3: Comparação entre M SJDBC e W JDBC, no contexto Actualização.
(b) SetupT
(a) ExecT
MSJDBC/WJDBC
1.45
MSJDBC/WJDBC
1.9
90
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.4
(c) Total
MSJDBC/WJDBC
35 linhas/thread
65 linhas/thread
90 linhas/thread
80
1.8
1.35
70
1.7
1.3
60
1.6
1.25
50
1.5
1.2
40
1.4
30
1.3
20
1.2
10
1.1
1.15
1.1
1.05
1
10
30
50
quantidade de threads
70
90 0
35 linhas/thread
65 linhas/thread
90 linhas/thread
1
10
30
50
quantidade de threads
70
90
10
30
50
70
90
quantidade de threads
O desempenho do W JDBC é sempre melhor, destacando-se para quantidades de threads
maiores. A alteração do valor de linhas por thread não provoca diferenças dignas de registo.
O tempo de preparação acentua a vantagem que o W JDBC tem sobre o JDBC no tempo
total.
5.2.1.2
Leitura
5.2.1.2.1
CJDBCI
A Figura 5.4 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCI , no contexto Leitura.
Figura 5.4: Comparação entre M SJDBC e CJDBCI , no contexto Leitura.
(b) SetupT
(a) ExecT
MSJDBC/CJDBCI
0.98
MSJDBC/CJDBCI
1.75
80
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.96
(c) Total
MSJDBC/CJDBCI
35 linhas/thread
65 linhas/thread
90 linhas/thread
70
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.7
0.94
60
1.65
0.9
50
1.6
0.88
40
1.55
0.92
0.86
1.5
30
0.84
1.45
20
0.82
0.8
1.4
10
0.78
10
30
50
quantidade de threads
70
90 0
1.35
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
O desempenho do CJDBCI é superior ao de JDBC para o tempo total, já no tempo de
42
execução verifica-se o contrário, embora a desvantagem não seja grande.
Nota-se uma descida do desempenho do CJDBCI em relação ao JDBC para quantidades
de threads maiores.
Os valores de número de linhas por thread maiores prejudicam um pouco o desempenho
do CJDBCI em relação ao desempenho do JDBC.
5.2.1.2.2
CJDBCS
A Figura 5.5 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCS , no contexto Leitura.
Figura 5.5: Comparação entre M SJDBC e CJDBCS , no contexto Leitura.
(a) ExecT
(b) SetupT
MSJDBC/CJDBCS
20
22
35 linhas/thread
65 linhas/thread
90 linhas/thread
18
(c) Total
MSJDBC/CJDBCS
MSJDBC/CJDBCS
20
35 linhas/thread
65 linhas/thread
90 linhas/thread
20
18
18
16
35 linhas/thread
65 linhas/thread
90 linhas/thread
16
16
14
14
14
12
12
12
10
10
10
8
8
8
6
6
6
4
4
4
2
2
10
30
50
quantidade de threads
70
90
2
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
Basicamente, o desempenho do CJDBCS é muito bom comparativamente ao de JDBC
sendo, para o tempo total, no mı́nimo 4 vezes melhor e no máximo consegue ser quase 20
vezes melhor.
Os valores do número de linhas por thread maiores penalizam mais o desempenho do
CJDBCS , embora não gravosamente.
O aumento da quantidade threads beneficia o desempenho do CJDBCS .
5.2.1.2.3
W JDBC
A Figura 5.6 apresenta os resultados da comparação do desempenho de M SJDBC e de
W JDBC, no contexto Leitura.
O desempenho do W JDBC é sempre muito melhor do que o desempenho do JDBC,
chegando a ser 10 vezes melhor do tempo total.
As quantidades de threads maiores beneficiam o desempenho do W JDBC.
O aumento do número de linhas por thread começa a dar vantagem ao desempenho de
W JDBC à medida que o número de threads também aumenta.
43
Figura 5.6: Comparação entre M SJDBC e W JDBC, no contexto Leitura.
(b) SetupT
(a) ExecT
70
7
(c) Total
MSJDBC/WJDBC
MSJDBC/WJDBC
MSJDBC/WJDBC
11
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
10
60
6
9
5
50
4
40
3
30
2
20
8
7
6
5
4
3
10
1
2
0
0
10
30
50
quantidade de threads
70
10
90
30
50
quantidade de threads
70
90
1
10
30
50
quantidade de threads
70
90
O tempo de preparação realça mais a vantagem do W JDBC no tempo total.
5.2.1.3
Inserção
5.2.1.3.1
CJDBCI
A Figura 5.7 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCI , no contexto Inserção.
Figura 5.7: Comparação entre M SJDBC e CJDBCI , no contexto Inserção.
(a) ExecT
(b) SetupT
MSJDBC/CJDBCI
1.06
(c) Total
MSJDBC/CJDBCI
MSJDBC/CJDBCI
1.12
40
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
35
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.1
1.04
30
1.08
25
1.06
20
1.04
1.02
1
1.02
15
0.98
1
10
0.96
0.98
5
0.94
10
30
50
quantidade de threads
70
90 0
0.96
10
30
50
quantidade de threads
70
90
10
30
50
70
90
quantidade de threads
Praticamente sempre, o desempenho do CJDBCI é ligeiramente superior ao de JDBC.
A atribuição de um maior número de linhas por thread, não tem grande peso para o
desempenho, e o facto da quantidade de threads aumentar dá uma pequena vantagem ao
CJDBCI .
44
Os resultados dos tempos de execução e total são semelhantes, pelo que se pode dizer que
o tempo de preparação tem pouca influência para o resultado final.
5.2.1.3.2
CJDBCS
A Figura 5.8 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCS , no contexto Inserção.
Figura 5.8: Comparação entre M SJDBC e CJDBCS , no contexto Inserção.
(b) SetupT
(a) ExecT
MSJDBC/CJDBCS
1.04
MSJDBC/CJDBCS
1.1
5.5
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.02
(c) Total
MSJDBC/CJDBCS
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
5
1
1.05
4.5
0.98
0.96
4
0.94
3.5
1
0.92
0.95
3
0.9
2.5
0.88
0.9
2
0.86
0.84
10
30
50
quantidade de threads
70
901.5
0.85
10
30
50
70
90
10
quantidade de threads
30
50
70
90
quantidade de threads
No geral o desempenho do CJDBCS é marginalmente melhor do que o desempenho do
JDBC.
Para um número de threads maior a vantagem de CJDBCS sobre JDBC acentua-se.
Os valores do número de linhas por thread menores apresentam um melhor desempenho
comparativo em favor de CJDBCS , do que valores maiores.
5.2.1.3.3
W JDBC
A Figura 5.9 apresenta os resultados da comparação do desempenho de M SJDBC e de
W JDBC, no contexto Inserção.
O desempenho do W JDBC é melhor para os valores do número de linhas por thread
menores, enquanto que para valores menores a vantagem é do JDBC.
O aumento da quantidade de threads não influencia com grande peso os resultados.
O tempo de preparação tem pouca influência no tempo total.
5.2.1.4
5.2.1.4.1
Remoção
CJDBCI
45
Figura 5.9: Comparação entre M SJDBC e W JDBC, no contexto Inserção.
(b) SetupT
(a) ExecT
MSJDBC/WJDBC
1.03
MSJDBC/WJDBC
1.08
60
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.02
(c) Total
MSJDBC/WJDBC
35 linhas/thread
65 linhas/thread
90 linhas/thread
55
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.07
50
1.06
45
1.05
40
1.04
35
1.03
1.01
1
0.99
0.98
30
0.97
25
1.02
1.01
1
20
0.96
0.99
15
0.95
0.98
10
0.94
10
30
50
70
90 5
quantidade de threads
0.97
10
30
50
quantidade de threads
70
10
90
30
50
70
90
quantidade de threads
A Figura 5.10 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCI , no contexto Remoção.
Figura 5.10: Comparação entre M SJDBC e CJDBCI , no contexto Remoção.
(b) SetupT
(a) ExecT
MSJDBC/CJDBCI
1.1
(c) Total
MSJDBC/CJDBCI
MSJDBC/CJDBCI
1.35
90
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
80
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.3
1.05
1.25
70
1.2
1
60
1.15
50
0.95
1.1
40
0.9
0.85
1.05
30
1
20
0.95
0.9
10
0.8
10
30
50
quantidade de threads
70
90 0
0.85
10
30
50
quantidade de threads
70
90
10
30
50
70
90
quantidade de threads
Para o tempo de execução o desempenho do CJDBCI fica um pouco abaixo do desempenho do JDBC. Embora a influencia seja pequena, o aumento da quantidade de threads penaliza um pouco o desempenho do CJDBCI , verificando-se o mesmo para os valores maiores
do número de linhas por thread.
O tempo de preparação é relevante para o resultado final; para o tempo do total o
CJDBCI apresenta melhor desempenho, sendo praticamente sempre superior a JDBC. O
aumento da quantidade de threads dá vantagem a CJDBCI e o aumento do valor do número
de linhas por thread penaliza um pouco o seu desempenho face ao desempenho do JDBC.
46
5.2.1.4.2
CJDBCS
A Figura 5.11 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCS , no contexto Remoção.
Figura 5.11: Comparação entre M SJDBC e CJDBCS , no contexto Remoção.
(b) SetupT
(a) ExecT
MSJDBC/CJDBCS
1.4
MSJDBC/CJDBCS
1.9
35
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.35
(c) Total
MSJDBC/CJDBCS
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.8
30
1.3
1.7
25
1.25
1.6
20
1.2
1.5
1.15
1.4
15
1.1
1.3
10
1.05
1.2
5
1
0.95
10
30
50
70
90 0
quantidade de threads
1.1
1
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
O desempenho do CJDBCS é superior ao de JDBC.
Para quantidades de threads menores o desempenho do CJDBCS é melhor mas a vantagem é moderada, para quantidades maiores o desempenho comparativo dispara atingindo
valores muito bons.
A quantidade de linhas por thread, não incute diferenças significativas.
O tempo de preparação tem influência no resultado, acentuando ainda mais a vantagem
do CJDBCS sobre o JDBC.
5.2.1.4.3
W JDBC
A Figura 5.12 apresenta os resultados da comparação do desempenho de M SJDBC e de
W JDBC, no contexto Remoção.
O desempenho do W JDBC é superior ao de JDBC.
O aumento da quantidade de threads beneficia o desempenho do W JDBC, embora seja
mais notório no tempo total.
Os valores de número de linhas por thread mais altos beneficiam mais o desempenho do
JDBC.
Os bons tempos de preparação do W JDBC dão-lhe ainda mais vantagem nos tempos
totais sobre o JDBC.
5.2.1.5
Resumo
Resumindo os resultados podemos dizer que em geral:
47
Figura 5.12: Comparação entre M SJDBC e W JDBC, no contexto Remoção.
(b) SetupT
(a) ExecT
MSJDBC/WJDBC
1.35
MSJDBC/WJDBC
1.9
90
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.3
(c) Total
MSJDBC/WJDBC
35 linhas/thread
65 linhas/thread
90 linhas/thread
80
1.8
1.25
70
1.7
1.2
60
1.6
1.15
50
1.5
40
1.4
30
1.3
20
1.2
10
1.1
1.1
1.05
1
0.95
0.9
10
30
50
70
quantidade de threads
90 0
35 linhas/thread
65 linhas/thread
90 linhas/thread
1
10
30
50
quantidade de threads
70
90
10
30
50
70
90
quantidade de threads
• O desempenho de CJDBC 1 e de W JDBC é superior ao desempenho de M SJDBC;
• O desempenho das soluções CJDBC e W JDBC melhora em relação ao desempenho
de M SJDBC com o aumento da quantidade de threads;
• O tempo de preparação de M SJDBC é muito superior, sendo por inúmeras vezes 80
vezes mais alto;
• Para o contexto Actualização as soluções CJDBCS e W JDBC apresentam o melhor
desempenho;
• Para o contexto Inserção a solução CJDBCI apresenta o melhor desempenho;
• Para o contexto Leitura a solução CJDBCS apresenta o melhor desempenho;
• Para o contexto Remoção as soluções CJDBCS e W JDBC apresentam o melhor desempenho.
5.2.2
Comparação com WJDBC
Esta secção apresenta os resultados do benchmark principal (secção 4.1). Os resultados
são apresentados sob a forma de uma comparação entre o desempenho registado para o
W JDBC e as soluções CJDBCI e CJDBCS . A comparação é realizada através de um
rácio dos tempos registados.
5.2.2.1
5.2.2.1.1
1
Actualização
CJDBCI
CJDBC refere-se às soluções CJDBCI e CJDBCS
48
A Figura 5.13 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCI , no contexto Actualização.
Figura 5.13: Comparação entre W JDBC e CJDBCI , no contexto Actualização.
(b) SetupT
(a) ExecT
WJDBC/CJDBCI
1
(c) Total
WJDBC/CJDBCI
0.98
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.96
0.95
WJDBC/CJDBCI
1
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.95
0.94
0.9
0.9
0.92
0.85
0.9
0.85
0.8
0.88
0.8
0.86
0.75
0.75
0.84
0.7
0.7
0.82
0.65
0.8
10
30
50
70
90
0.65
10
quantidade de threads
30
50
70
90
10
quantidade de threads
30
50
70
90
quantidade de threads
O desempenho do W JDBC é sempre melhor do que o desempenho do CJDBCI .
Para os tempos de execução e total o aumento da quantidade de threads penaliza mais
o desempenho do CJDBCI , enquanto que para o tempo de preparação a mesma situação
beneficia o desempenho do CJDBCI .
Um número maior de linhas por thread não implica alterações importantes na comparação
dos desempenhos.
O tempo de preparação tem pouco impacto no tempo total.
5.2.2.1.2
CJDBCS
A Figura 5.14 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCS , no contexto Actualização.
Ambos têm desempenhos semelhantes, mas no tempo de preparação a vantagem está
claramente do lado do W JDBC, aumentando com o crescimento da quantidade de threads.
Porém este tempo não altera muito o total.
O número de threads ou de linhas por thread não afecta significativamente os resultados.
5.2.2.2
5.2.2.2.1
Leitura
CJDBCI
A Figura 5.15 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCI , no contexto Leitura.
O desempenho do CJDBCI é superior ao de W JDBC para a quantidade de threads
menor e número de linhas por thread maior. O W JDBC é bastante superior para as restantes
49
Figura 5.14: Comparação entre W JDBC e CJDBCS , no contexto Actualização.
(b) SetupT
(a) ExecT
WJDBC/CJDBCS
1.045
(c) Total
WJDBC/CJDBCS
WJDBC/CJDBCS
1.035
0.42
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.41
1.04
1.03
0.4
1.025
0.39
1.02
0.38
1.015
0.37
1.01
0.36
1.005
0.35
1
1.035
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.03
1.025
1.02
1.015
1.01
10
30
50
70
90
0.995
0.34
10
30
quantidade de threads
50
70
10
90
30
quantidade de threads
50
quantidade de threads
70
90
70
90
Figura 5.15: Comparação entre W JDBC e CJDBCI , no contexto Leitura.
(b) SetupT
(a) ExecT
1.1
1.4
(c) Total
WJDBC/CJDBCI
WJDBC/CJDBCI
WJDBC/CJDBCI
1.4
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.2
1.05
1.2
1
1
1
0.8
0.95
0.8
0.6
0.9
0.6
0.4
0.85
0.4
0.8
0.2
0.2
0.75
0
10
30
50
70
90
quantidade de threads
35 linhas/thread
65 linhas/thread
90 linhas/thread
10
30
50
quantidade de threads
70
90 0
10
30
50
quantidade de threads
situações.
Uma quantidade de thread maior afecta negativamente o desempenho de CJDBCI .
O número de linhas por thread tem pouco efeito para as quantidades de threads maiores.
O tempo de preparação é semelhante, havendo uma ligeira vantagem para o W JDBC, e
tem pouca preponderância no resultado total.
5.2.2.2.2
CJDBCS
A Figura 5.16 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCS , no contexto Leitura.
O CJDBCS começa por apresentar um desempenho bastante superior, mas que se degrada rapidamente com o aumento do número de threads. Porém o desempenho de CJDBCS
50
Figura 5.16: Comparação entre W JDBC e CJDBCS , no contexto Leitura.
(b) SetupT
(a) ExecT
0.4
8
(c) Total
WJDBC/CJDBCS
WJDBC/CJDBCS
WJDBC/CJDBCS
3.5
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.38
7
3
0.36
6
2.5
0.34
5
0.32
4
2
0.3
3
1.5
0.28
2
0.26
10
1
10
30
50
70
30
50
70
90 1
quantidade de threads
90
10
30
quantidade de threads
50
quantidade de threads
70
90
mantém-se superior ao de W JDBC.
Os números de linhas por thread maiores favorecem o desempenho do CJDBCS nos
tempos de execução e total, e prejudicam-no no tempo de preparação.
O mau tempo de preparação do CJDBCS faz com que a grande vantagem que tinha no
tempo de execução seja reduzida quase para metade no tempo total.
5.2.2.3
Inserção
5.2.2.3.1
CJDBCI
A Figura 5.17 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCI , no contexto Inserção.
Figura 5.17: Comparação entre W JDBC e CJDBCI , no contexto Inserção.
(b) SetupT
(a) ExecT
WJDBC/CJDBCI
1.08
0.8
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.06
(c) Total
WJDBC/CJDBCI
0.75
1.04
WJDBC/CJDBCI
1.06
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.04
0.7
1.02
0.65
1
0.6
0.98
0.55
0.96
0.5
0.94
0.45
0.92
1.02
1
0.98
0.96
0.94
0.92
0.9
0.4
10
30
50
quantidade de threads
70
90
0.9
10
30
50
quantidade de threads
51
70
90
10
30
50
quantidade de threads
70
90
O desempenho do W JDBC é superior ao de CJDBCI para quantidades de threads mais
baixas, enquanto que é o CJDBCI que ganha para as quantidades mais altas, mas com uma
ligeira vantagem.
O tempo de preparação do W JDBC é significativamente melhor, principalmente para os
valores de número de linhas mais baixos.
No geral o número de linhas por thread não afecta significativamente a comparação.
5.2.2.3.2
CJDBCS
A Figura 5.18 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCS , no contexto Inserção.
Figura 5.18: Comparação entre W JDBC e CJDBCS , no contexto Inserção.
(a) ExecT
(b) SetupT
WJDBC/CJDBCS
1.06
0.35
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.04
(c) Total
WJDBC/CJDBCS
WJDBC/CJDBCS
1.06
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.04
0.3
1.02
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.02
1
1
0.25
0.98
0.98
0.96
0.96
0.2
0.94
0.94
0.92
0.92
0.15
0.9
0.9
0.88
0.88
0.1
0.86
0.86
0.84
0.05
10
30
50
70
90
0.84
10
quantidade de threads
30
50
70
90
10
30
quantidade de threads
50
70
90
quantidade de threads
O desempenho do W JDBC é superior ao de CJDBCS para as quantidades de threads
menores, e o desempenho do CJDBCS é superior para as quantidades maiores.
O tempo de preparação do CJDBCS é muito pior do que o tempo do W JDBC, e vai-se
degradando com uma quantidade de threads maior.
O tempo de preparação tem um impacto pequeno no resultado final.
O número de linhas por thread não influencia significativamente os resultados.
5.2.2.4
5.2.2.4.1
Remoção
CJDBCI
A Figura 5.19 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCI , no contexto Remoção.
O desempenho do W JDBC é melhor do que o desempenho do CJDBCI , cujo desempenho se degrada com o aumento da quantidade de threads.
O número de linhas por thread não afecta significativamente os resultados.
52
Figura 5.19: Comparação entre W JDBC e CJDBCI , no contexto Remoção.
(b) SetupT
(a) ExecT
WJDBC/CJDBCI
0.95
(c) Total
WJDBC/CJDBCI
1
35 linhas/thread
65 linhas/thread
90 linhas/thread
WJDBC/CJDBCI
0.95
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.95
0.9
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.9
0.9
0.85
0.85
0.85
0.8
0.8
0.75
0.8
0.7
0.75
0.75
0.65
0.6
0.7
0.7
0.55
0.65
0.5
10
30
50
70
90
0.65
10
30
quantidade de threads
50
70
90
10
30
quantidade de threads
50
70
90
quantidade de threads
O tempo de preparação é semelhante para ambos, tendo pouco impacto no resultado
total.
5.2.2.4.2
CJDBCS
A Figura 5.20 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCS , no contexto Remoção.
Figura 5.20: Comparação entre W JDBC e CJDBCS , no contexto Remoção.
(a) ExecT
(b) SetupT
WJDBC/CJDBCS
1.08
0.41
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.06
(c) Total
WJDBC/CJDBCS
WJDBC/CJDBCS
1.08
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.06
0.4
1.04
1.04
1.02
0.39
1.02
1
1
0.38
0.98
0.98
0.96
0.37
0.96
0.94
0.94
0.36
0.92
0.92
0.9
0.35
0.9
0.88
0.88
0.34
10
30
50
quantidade de threads
70
90
0.86
10
30
50
quantidade de threads
70
90
10
30
50
70
90
quantidade de threads
Nos tempos de execução e total a vantagem vai para o desempenho do CJDBCS , embora
seja muito pequena. O aumento da quantidade de threads não influencia significativamente os
resultados e o número de linhas por thread maior é melhor para o desempenho do W JDBC.
O tempo de preparação do W JDBC é bastante superior ao de CJDBCS , e o aumento
de quantidade de threads melhora a vantagem.
53
O tempo de preparação não tem muito peso no tempo total, pelo que os resultados da
comparação dos tempos de execução e total são muito parecidos.
5.2.3
Resumo
Resumindo os resultados podemos dizer que em geral:
• O desempenho de CJDBCI é pior do que o de W JDBC, principalmente para quantidades de threads maiores. A excepção regista-se no contexto da Inserção em que
CJDBCI é ligeiramente melhor;
• O desempenho de CJDBCS é melhor do que o de W JDBC, principalmente para
quantidades de threads maiores.
5.3
Benchmark com atrasos
Nesta secção são apresentados os resultados do benchmark que introduz a simulação de
processamento entre colunas ou linhas. Este benchmark foi apresentado na secção 4.2.
Uma vez que o único tempo medido que é directamente afectado pela introdução de
atrasos é o tempo de execução, este será o único cujos valores serão apresentados. Para ser
mais cómodo, os gráficos dos resultados com atraso e sem atraso são agrupados por contexto.
Os valores dos atrasos entre colunas e linhas foram escolhidos de forma a poderem produzir
resultados que sejam comparáveis. A tabela de testes (fig. 4.1) possui oito colunas, assim no
atraso entre colunas é introduzido um atraso total de 8 × atrasoC , em que atrasoC é valor do
atraso introduzido entre cada coluna. O atraso entre linhas (atrasoL ), para ser comparável,
terá então o valor de 8 × atrasoC . Por exemplo, atraso de 0.1ms entre colunas versus atraso
de 0.8ms entre linhas.
Os detalhes do funcionamento deste benchmark podem ser encontrados na secção 4.2.
5.3.1
Atraso entre colunas
5.3.1.1
Comparação com MSJDBC
Esta secção apresenta os resultados do benchmark com atrasos (secção 4.2). Os resultados
reflectem o efeito do atraso entre colunas, e são apresentados sob a forma de uma comparação
entre o desempenho registado para o M SJDBC e as restantes soluções. A comparação é
realizada através de um rácio dos tempos registados.
5.3.1.1.1
Actualização
5.3.1.1.1.1
CJDBCI
A Figura 5.21 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCI , no contexto Actualização.
54
Figura 5.21: Efeito do atraso na comparação entre M SJDBC e CJDBCI , no contexto
Actualização.
(b) Atraso de 1ms
(a) Sem atraso
MSJDBC/CJDBCI
1.06
MSJDBC/CJDBCI
1.06
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.04
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.04
1.02
1.02
1
1
0.98
0.98
0.96
0.96
0.94
0.94
0.92
0.92
0.9
0.9
0.88
0.88
0.86
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
A introdução afectou ligeiramente o desempenho de CJDBCI , diminuindo o desempenho
em relação ao M SJDBC.
Os números de linhas por thread mais baixos denotam um maior efeito do atraso no
desempenho de CJDBCI ;
Os números de linhas por thread mais altos refletem menos impacto no desempenho.
No geral a introdução de atraso entre colunas na actualização não tem muita influência
nos resultados.
5.3.1.1.1.2
CJDBCS
A Figura 5.22 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCS , no contexto Actualização.
A introdução de atraso prejudica um pouco o desempenho do CJDBCS .
Onde se nota um maior impacto da introdução de atraso é no número de linhas mais
baixo, e para quantidades de threads maiores.
No geral a introdução de atraso entre colunas na actualização não tem muita influência
nos resultados, e é ainda menos influenciante do que no caso do CJDBCI .
5.3.1.1.1.3
W JDBC
A Figura 5.23 apresenta os resultados da comparação do desempenho de M SJDBC e de
W JDBC, no contexto Actualização.
A introdução de atraso tem uma enorme influência nos resultados, prejudicando gravemente o desempenho do W JDBC.
55
Figura 5.22: Efeito do atraso na comparação entre M SJDBC e CJDBCS , no contexto
Actualização.
(b) Atraso de 1ms
(a) Sem atraso
MSJDBC/CJDBCS
1.5
MSJDBC/CJDBCS
1.35
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.45
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.3
1.4
1.25
1.35
1.3
1.2
1.25
1.15
1.2
1.15
1.1
1.1
1.05
1.05
1
1
10
30
50
70
90
10
30
quantidade de threads
50
70
90
quantidade de threads
Figura 5.23: Efeito do atraso na comparação entre M SJDBC e W JDBC, no contexto
Actualização.
(a) Sem atraso
(b) Atraso de 1ms
MSJDBC/WJDBC
MSJDBC/WJDBC
0.22
1.45
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.4
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.215
0.21
1.35
0.205
1.3
0.2
1.25
0.195
0.19
1.2
0.185
1.15
0.18
1.1
0.175
1.05
0.17
0.165
1
10
30
50
quantidade de threads
70
90
10
30
50
70
90
quantidade de threads
Na ausência de atraso o desempenho do W JDBC é superior ao de M SJDBC, mas com
a introdução de atraso o desempenho do W JDBC é bastante pior (cerca 8 vezes).
Porém comportamento em resposta à variação dos parâmetros é semelhante: os números
de linhas por thread mais baixos e quantidades de threads maiores favorecem o desempenho
do W JDBC.
5.3.1.1.2
Leitura
56
5.3.1.1.2.1
CJDBCI
A Figura 5.24 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCI , no contexto Leitura.
Figura 5.24: Efeito do atraso na comparação entre M SJDBC e CJDBCI , no contexto Leitura.
(b) Atraso de 1ms
(a) Sem atraso
MSJDBC/CJDBCI
0.98
MSJDBC/CJDBCI
1
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.96
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.98
0.94
0.96
0.92
0.94
0.9
0.92
0.88
0.9
0.86
0.88
0.84
0.86
0.82
0.84
0.8
0.82
0.78
0.8
10
30
50
70
90
quantidade de threads
10
30
50
70
90
quantidade de threads
A introdução de atraso beneficia ligeiramente o desempenho do CJDBCI em relação ao
M SJDBC.
São as quantidades de linhas por thread maiores que mais ganham com a introdução de
atraso.
Porém o desempenho do CJDBCI degrada-se com o aumento da quantidade de threads.
5.3.1.1.2.2
CJDBCS
A Figura 5.25 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCS , no contexto Leitura.
O desempenho do CJDBCS é gravemente prejudicado pela introdução de atraso.
Ainda existe superioridade do desempenho do CJDBCS em relação ao M SJDBC, mas
essa superioridade é cerca de 4.5 vezes mais pequena em relação à ausência de atraso.
O CJDBCS continua a beneficiar do aumento da quantidade de threads.
O número de linhas por thread varia em quase nada os resultados, pois para todos os
valores os resultados são aproximados.
5.3.1.1.2.3
W JDBC
A Figura 5.26 apresenta os resultados da comparação do desempenho de M SJDBC e de
W JDBC, no contexto Leitura.
57
Figura 5.25: Efeito do atraso na comparação entre M SJDBC e CJDBCS , no contexto Leitura.
(b) Atraso de 1ms
(a) Sem atraso
MSJDBC/CJDBCS
MSJDBC/CJDBCS
4
20
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
18
3.5
16
3
14
12
2.5
10
2
8
6
1.5
4
1
2
10
30
50
quantidade de threads
70
10
90
30
50
quantidade de threads
70
90
Figura 5.26: Efeito do atraso na comparação entre M SJDBC e W JDBC, no contexto Leitura.
(a) Sem atraso
(b) Atraso de 1ms
MSJDBC/WJDBC
MSJDBC/WJDBC
0.6
7
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
6
0.5
5
0.4
4
0.3
3
0.2
2
0.1
1
0
0
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
O desempenho de W JDBC em relação ao de M SJDBC diminuiu drasticamente com a
introdução de atraso.
Para as quantidades de threads menores a tendência é o desempenho de W JDBC diminui
em relação ao de M SJDBC. Para as quantidades maiores a relação entre as duas soluções
mantém-se quase constante.
58
5.3.1.2
Comparação com WJDBC
Esta secção apresenta os resultados do benchmark com atrasos (secção 4.2). Os resultados
reflectem o efeito do atraso entre colunas, e são apresentados sob a forma de uma comparação
entre o desempenho registado para o W JDBC e as restantes soluções. A comparação é
realizada através de um rácio dos tempos registados.
5.3.1.2.1
Actualização
5.3.1.2.1.1
CJDBCI
A Figura 5.27 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCI , no contexto Actualização.
Figura 5.27: Efeito do atraso na comparação entre W JDBC e CJDBCI , no contexto
Actualização.
(b) Atraso de 1ms
(a) Sem atraso
WJDBC/CJDBCI
1
WJDBC/CJDBCI
6
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
5.8
0.95
5.6
0.9
5.4
5.2
0.85
5
0.8
4.8
0.75
4.6
4.4
0.7
4.2
0.65
10
30
50
quantidade de threads
70
90 4
10
30
50
quantidade de threads
70
90
A introdução de atraso dá uma enorme vantagem ao CJDBCI .
Sem o atraso o desempenho do CJDBCI é no melhor caso aproximadamente igual ao
de W JDBC e no pior caso é cerca de 0.3 vezes pior, porém com a introdução de atraso no
melhor caso é 6 vezes melhor e no pior caso 4 vezes melhor.
O CJDBCI continua a perder desempenho face ao W JDBC para quantidades de threads
maiores, e os números de linhas por thread mais altos também continuam a beneficiar o
CJDBCI .
5.3.1.2.1.2
CJDBCS
A Figura 5.28 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCS , no contexto Actualização.
59
Figura 5.28: Efeito do atraso na comparação entre W JDBC e CJDBCS , no contexto
Actualização.
(b) Atraso de 1ms
(a) Sem atraso
WJDBC/CJDBCS
1.045
WJDBC/CJDBCS
6.6
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
6.5
1.04
6.4
1.035
6.3
1.03
6.2
1.025
6.1
1.02
6
1.015
5.9
1.01
10
30
50
quantidade de threads
70
905.8
10
30
50
quantidade de threads
70
90
O desempenho do CJDBCS é bastante beneficiado pela introdução de atraso, em relação
ao W JDBC. Sem atraso, o desempenho de ambos é próximo. Com atraso, o desempenho
de CJDBCS é mais de 6 vezes melhor.
A variação os parâmetros do benchmark continuam a não influenciar significativamente
os resultados.
5.3.1.2.2
Leitura
5.3.1.2.2.1
CJDBCI
A Figura 5.29 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCI , no contexto Leitura.
O impacto da introdução de atraso é enorme, dando uma grande vantagem ao desempenho do CJDBCI sobre o W JDBC.
É maior a vantagem que o CJDBCI tem sobre o W JDBC com atraso, do que a vantagem
que existe do W JDBC na ausência de atraso.
Sem atraso o desempenho do W JDBC chega a ser 1.4 vezes melhor, mas com atraso o
desempenho do CJDBCI chega a ser cerda de 19 vezes melhor.
A tendência é de melhoria do desempenho de CJDBCI com o aumento de quantidade
de threads até se verificar uma certa estabilização para quantidades maiores.
Quanto maior é o número de linhas por thread, maior é a vantagem do CJDBCI sobre
W JDBC.
60
Figura 5.29: Efeito do atraso na comparação entre W JDBC e CJDBCI , no contexto Leitura.
(b) Atraso de 1ms
(a) Sem atraso
WJDBC/CJDBCI
1.4
WJDBC/CJDBCI
20
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
18
1.2
16
1
14
12
0.8
10
0.6
8
6
0.4
4
0.2
2
0
10
5.3.1.2.2.2
30
50
quantidade de threads
70
90
0
10
30
50
quantidade de threads
70
90
CJDBCS
A Figura 5.30 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCS , no contexto Leitura.
Figura 5.30: Efeito do atraso na comparação entre W JDBC e CJDBCS , no contexto Leitura.
(b) Atraso de 1ms
(a) Sem atraso
WJDBC/CJDBCS
WJDBC/CJDBCS
70
8
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
7
60
6
50
5
40
4
30
3
20
10
2
0
1
10
30
50
70
90
quantidade de threads
10
30
50
70
90
quantidade de threads
Verifica-se um resultado semelhante ao de CJDBCI , mas a vantagem de CJDBCS sobre
W JDBC é ainda mais esmagadora.
O desempenho de CJDBCS já era melhor do que o desempenho de W JDBC na ausência
de atraso, por isso a vantagem foi ainda maior com atraso.
61
A tendência de perda de desempenho do CJDBCS com o aumento da quantidade de
threads foi invertida.
5.3.2
Atraso entre linhas
5.3.2.1
Comparação com MSJDBC
Esta secção apresenta os resultados do benchmark com atrasos (secção 4.2). Os resultados
reflectem o efeito do atraso entre linhas, e são apresentados sob a forma de uma comparação
entre o desempenho registado para o M SJDBC e as restantes soluções. A comparação é
realizada através de um rácio dos tempos registados.
5.3.2.1.1
Actualização
5.3.2.1.1.1
CJDBCI
A Figura 5.31 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCI , no contexto Actualização.
Figura 5.31: Efeito do atraso na comparação entre M SJDBC e CJDBCI , no contexto
Actualização.
(a) Sem atraso
(b) Atraso de 8ms
MSJDBC/CJDBCI
1.06
MSJDBC/CJDBCI
1.02
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.04
35 linhas/thread
65 linhas/thread
90 linhas/thread
1
1.02
0.98
1
0.96
0.98
0.94
0.96
0.92
0.94
0.9
0.92
0.88
0.9
0.86
0.88
0.84
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
Os resultados do desempenho do CJDBCI com atraso são ligeiramente piores do que os
resultados do M SJDBC.
A quantidade de linhas por thread mais baixa é a que revela maior perda de desempenho
por parte do CJDBCI .
Tirando a ligeira descida do desempenho do CJDBCI , o efeito do atraso é insignificante.
62
5.3.2.1.1.2
CJDBCS
A Figura 5.32 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCS , no contexto Actualização.
Figura 5.32: Efeito do atraso na comparação entre M SJDBC e CJDBCS , no contexto
Actualização.
(a) Sem atraso
(b) Atraso de 8ms
MSJDBC/CJDBCS
1.5
MSJDBC/CJDBCS
1.3
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.45
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.25
1.4
1.35
1.2
1.3
1.25
1.15
1.2
1.1
1.15
1.1
1.05
1.05
1
1
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
O desempenho do CDJBCS é prejudicado ligeiramente com introdução de atraso.
É no número de linhas por thread mais baixo que se nota a maior diferença.
No geral o efeito da introdução de atraso é insignificante.
5.3.2.1.1.3
W JDBC
A Figura 5.33 apresenta os resultados da comparação do desempenho de M SJDBC e de
W JDBC, no contexto Actualização.
O desempenho do W JDBC sofre uma perda significativa em relação ao desempenho do
M SJDBC.
O crescimento da vantagem em relação ao M SJDBC quando o número de threads aumenta já não se verifica.
Os valores do desempenho do W JDBC para os números de linhas por thread maiores
perdem mais, ficando mesmo abaixo do desempenho do M SJDBC.
5.3.2.1.2
Leitura
5.3.2.1.2.1
CJDBCI
63
Figura 5.33: Efeito do atraso na comparação entre M SJDBC e W JDBC, no contexto
Actualização.
(b) Atraso de 8ms
(a) Sem atraso
MSJDBC/WJDBC
MSJDBC/WJDBC
1.45
1.15
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.4
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.1
1.35
1.05
1.3
1
1.25
0.95
1.2
0.9
1.15
0.85
1.1
0.8
1.05
0.75
1
10
30
50
70
10
90
30
quantidade de threads
50
quantidade de threads
70
90
A Figura 5.34 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCI , no contexto Leitura.
Figura 5.34: Efeito do atraso na comparação entre M SJDBC e CJDBCI , no contexto Leitura.
(a) Sem atraso
(b) Atraso de 8ms
MSJDBC/CJDBCI
0.98
MSJDBC/CJDBCI
1
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.96
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.98
0.96
0.94
0.94
0.92
0.92
0.9
0.9
0.88
0.88
0.86
0.86
0.84
0.84
0.82
0.82
0.8
0.8
0.78
0.78
10
30
50
70
90
quantidade de threads
10
30
50
70
90
quantidade de threads
A introdução de atraso favorece o desempenho do CJDBCI em relação ao desempenho
do M SJDBC.
É nos valores de linhas por thread mais altos que se verifica a vantagem da introdução de
atraso de CJDBCI em relação a M SJDBC, no entanto o desempenho do M SJDBC ainda
é superior ao de CJDBCI .
Os diferentes valores de linhas por thread apresentam resultados próximos entre si.
64
As quantidades maiores de threads penalizam o desempenho do CJDBCI .
5.3.2.1.2.2
CJDBCS
A Figura 5.35 apresenta os resultados da comparação do desempenho de M SJDBC e de
CJDBCS , no contexto Leitura.
Figura 5.35: Efeito do atraso na comparação entre M SJDBC e CJDBCS , no contexto Leitura.
(a) Sem atraso
(b) Atraso de 8ms
MSJDBC/CJDBCS
MSJDBC/CJDBCS
6
20
35 linhas/thread
65 linhas/thread
90 linhas/thread
18
35 linhas/thread
65 linhas/thread
90 linhas/thread
5.5
5
16
4.5
14
4
12
3.5
10
3
8
2.5
6
2
4
1.5
1
2
10
30
50
70
90
10
30
quantidade de threads
50
quantidade de threads
70
90
O efeito da introdução do atraso é penalizador para o desempenho do CJDBCS .
Embora o CJDBCS ainda mantenha um desempenho claramente superior ao desempenho
de M SJDBC, a vantagem é aproximadamente 3.5 vezes menor.
A diferença entre os resultados para os vários valores de linhas por thread diminuiu.
O aumento da quantidade de threads continua a beneficiar o desempenho do CJDBCS .
5.3.2.1.2.3
W JDBC
A Figura 5.36 apresenta os resultados da comparação do desempenho de M SJDBC e de
W JDBC, no contexto Leitura.
A introdução de atraso é muito penalizador para o desempenho do W JDBC.
O desempenho do W JDBC deixou de ser superior ao de M SJDBC, e passou a ser
bastante inferior.
W JDBC perdeu mais desempenho para os valores de linhas por thread maiores.
O aumento da quantidade de thread começa por penalizar o desempenho do W JDBC
até que estabiliza, permanecendo relativamente constante.
65
Figura 5.36: Efeito do atraso na comparação entre M SJDBC e W JDBC, no contexto Leitura.
(b) Atraso de 8ms
(a) Sem atraso
MSJDBC/WJDBC
MSJDBC/WJDBC
1.1
7
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
1
6
0.9
5
0.8
4
0.7
0.6
3
0.5
2
0.4
1
0.3
0.2
0
10
5.3.2.2
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
Comparação com WJDBC
Esta secção apresenta os resultados do benchmark com atrasos (secção 4.2). Os resultados
reflectem o efeito do atraso entre linhas, e são apresentados sob a forma de uma comparação
entre o desempenho registado para o W JDBC e as restantes soluções. A comparação é
realizada através de um rácio dos tempos registados.
5.3.2.2.1
Actualização
5.3.2.2.1.1
CJDBCI
A Figura 5.37 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCI , no contexto Actualização.
A introdução de atraso beneficia o desempenho do CJDBCI em relação ao desempenho
do W JDBC, principalmente para os valores de linhas por thread maiores.
O valor de linhas por thread mais baixo sofreu poucas alterações.
Tirando uma fase inicial em que o aumento da quantidade de threads beneficia o desempenho do CJDBCI , o aumento da quantidade de thread penaliza o desempenho do CJDBCI
em relação ao desempenho do W JDBC.
5.3.2.2.1.2
CJDBCS
A Figura 5.38 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCS , no contexto Actualização.
66
Figura 5.37: Efeito do atraso na comparação entre W JDBC e CJDBCI , no contexto
Actualização.
(b) Atraso de 8ms
(a) Sem atraso
WJDBC/CJDBCI
1
WJDBC/CJDBCI
1.25
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.2
0.95
1.15
0.9
1.1
1.05
0.85
1
0.8
0.95
0.9
0.75
0.85
0.7
0.8
0.65
0.75
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90
Figura 5.38: Efeito do atraso na comparação entre W JDBC e CJDBCS , no contexto
Actualização.
(a) Sem atraso
(b) Atraso de 8ms
WJDBC/CJDBCS
1.045
WJDBC/CJDBCS
1.45
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.4
1.04
1.35
1.035
1.3
1.03
1.25
1.2
1.025
1.15
1.02
1.1
1.015
1.05
1.01
10
30
50
quantidade de threads
70
90
1
10
30
50
quantidade de threads
70
90
A introdução de atraso beneficia com algum significância o desempenho do CJDBCS face
ao desempenho do W JDBC, principalmente para os valores de linhas por thread maiores.
Inicialmente o aumento da quantidade threads dá alguma vantagem ao desempenho do
CJDBCS , até estabilizar e manter-se relativamente constante para as quantidades maiores.
5.3.2.2.2
Leitura
67
5.3.2.2.2.1
CJDBCI
A Figura 5.39 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCI , no contexto Leitura.
Figura 5.39: Efeito do atraso na comparação entre W JDBC e CJDBCI , no contexto Leitura.
(a) Sem atraso
(b) Atraso de 8ms
WJDBC/CJDBCI
1.4
WJDBC/CJDBCI
3.5
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.2
35 linhas/thread
65 linhas/thread
90 linhas/thread
3
1
2.5
0.8
2
0.6
1.5
0.4
1
0.2
0
10
30
50
quantidade de threads
70
0.5
90 10
30
50
quantidade de threads
70
90
Com a introdução de atraso, o desempenho do CJDBCI passa a ser sempre superior ao
de W JDBC; a vantagem que CJDBCI tem agora em relação ao W JDBC é maior do que
a vantagem que o W JDBC tinha na ausência de atraso.
Os valores do número de linhas por thread maiores dão maior vantagem ao desempenho
do CJDBCI .
O aumento do quantidade de threads dá alguma vantagem ao desempenho do CJDBCI ,
para quantidades menores.
5.3.2.2.2.2
CJDBCS
A Figura 5.40 apresenta os resultados da comparação do desempenho de W JDBC e de
CJDBCS , no contexto Leitura.
O desempenho do CJDBCS em relação ao W JDBC beneficia bastante da introdução de
atraso.
Para além de o desempenho do CJDBCS face ao de W JDBC ser ainda maior do que
o que se verificava na ausência de atraso, o aumento da quantidade de threads deixa de ser
penalizador para o desempenho do CJDBCS e passa a dar vantagem.
68
Figura 5.40: Efeito do atraso na comparação entre W JDBC e CJDBCS , no contexto Leitura.
(b) Atraso de 8ms
(a) Sem atraso
WJDBC/CJDBCS
WJDBC/CJDBCS
18
8
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
16
7
14
6
12
5
10
8
4
6
3
4
2
2
0
1
10
30
50
70
90
10
quantidade de threads
5.3.3
30
50
quantidade de threads
70
90
Resumo
O atraso introduzido entre colunas provocou mais efeito nos resultados do que o atraso
entre linhas, mas produzem conclusões semelhantes, por isso o seguinte é válido para ambos
(salvo diferenças referidas explicitamente):
• A introdução de atraso afecta o M SJDBC e o CJDBCI da mesma forma, pelo que
os resultados com e sem atraso são semelhantes;
• A introdução de atraso não afectou significativamente os resultados da comparação de
CJDBCS com M SJDBC na Actualização, no entanto para a Leitura o desempenho
de CJDBCS sofreu um grande decréscimo;
• O desempenho da solução W JDBC diminuiu muito com a introdução de atraso. Por
exemplo, comparando com a ausência de atraso entre colunas, na Leitura foi cerca de
10 vezes pior e na Actualização foi cerca de 6 vezes pior.
5.4
Cache individual vs Cache partilhado
Esta secção apresenta os resultados para o benchmark do Cache individual vs Cache
partilhado (secção 4.3). Para cada tamanho do fetch size é realizada a comparação entre
os resultados registados para o cache individual e o cache partilhado, através de um rácio.
Relembro que para poder ser possı́vel a realização deste benchmark teve de ser criada uma
versão especial do cache partilhado, denominada de CJDBCS M , que permite definir diferentes tamanhos para a capacidade do cache. Para se ter uma noção da diferença de desempenho entre as versões oficiais de cada tipo de cache, é realizada uma comparação adicional
69
entre CJDBCI e CJDBCS , mas que obviamente só foi executada no contexto de fetch size
100%.
Relembro ainda que se utilizou a forma mais compacta 10, 20, etc, para designar os
contextos fetch size 10%, fetch size 20%, etc.
5.4.1
Fetch size 10%
5.4.1.1
CJDBCS M
A Figura 5.41 apresenta os resultados da comparação do desempenho de CJDBCI e de
CJDBCS M , no contexto Fetch size 10%.
Figura 5.41: Comparação entre CJDBCI e CJDBCS M , no contexto 10 .
(a) ExecT
(b) SetupT
1.35
0.7
(c) Total
CJDBCI/CJDBCSM
CJDBCI/CJDBCSM
0.6
1.3
0.5
1.25
0.4
1.2
0.3
1.15
0.2
1.1
CJDBCI/CJDBCSM
0.8
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.7
0.6
0.5
0.4
0.3
0.2
1.05
0.1
0.1
1
0
10
30
50
70
90
10
30
quantidade de threads
50
quantidade de threads
70
90 0
10
30
50
70
90
quantidade de threads
O desempenho do CJDBCS M é bastante inferior ao desempenho do CJDBCI .
O valor do tempo de preparação tem pouco impacto, pelo que os valores do desempenho
comparativo para os tempos de execução e total são essencialmente os mesmos.
5.4.2
5.4.2.1
Fetch size 20%
CJDBCS M
A Figura 5.42 apresenta os resultados da comparação do desempenho de CJDBCI e de
CJDBCS M , no contexto Fetch size 20%.
O desempenho do CJDBCS M é sempre bastante inferior ao de CJDBCI . O aumento do
número de linhas por thread penaliza significativamente o desempenho do CJDBCS M face
70
Figura 5.42: Comparação entre CJDBCI e CJDBCS M , no contexto 20.
(b) SetupT
(a) ExecT
CJDBCI/CJDBCSM
0.26
1.25
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.24
(c) Total
CJDBCI/CJDBCSM
CJDBCI/CJDBCSM
0.3
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.2
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.25
0.22
1.15
0.2
1.1
0.2
0.18
1.05
0.16
0.15
1
0.14
0.95
0.12
0.1
0.9
0.1
0.08
0.85
10
30
50
70
90
0.05
10
30
50
quantidade de threads
70
90
10
30
quantidade de threads
50
70
90
quantidade de threads
ao CJDBCI . Embora o aumento da quantidade de threads também penalize o desempenho
do CJDBCS M , o peso é menor. O tempo de preparação tem pouca influência no resultado
total.
5.4.3
Fetch size 50%
5.4.3.1
CJDBCS M
A Figura 5.43 apresenta os resultados da comparação do desempenho de CJDBCI e de
CJDBCS M , no contexto Fetch size 50%.
Figura 5.43: Comparação entre CJDBCI e CJDBCS M , no contexto 50.
(b) SetupT
(a) ExecT
CJDBCI/CJDBCSM
0.35
(c) Total
CJDBCI/CJDBCSM
1.16
35 linhas/thread
65 linhas/thread
90 linhas/thread
CJDBCI/CJDBCSM
0.4
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.14
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.35
0.3
1.12
0.3
0.25
1.1
1.08
0.25
1.06
0.2
0.2
0.15
1.04
0.15
1.02
0.1
0.1
1
0.05
0.98
10
30
50
quantidade de threads
70
90
0.05
10
30
50
quantidade de threads
70
90
10
30
50
70
90
quantidade de threads
A comparação de desempenho dos dois mantém-se semelhante aos contextos fetch size
71
10% e 20%, pelo que o que foi dito para esses contextos aplica-se a este.
Porém nota-se uma muito ligeira melhoria em relação ao contexto fetch size 20%.
5.4.4
Fetch size 75%
5.4.4.1
CJDBCS M
A Figura 5.44 apresenta os resultados da comparação do desempenho de CJDBCI e de
CJDBCS M , no contexto Fetch size 75%.
Figura 5.44: Comparação entre CJDBCI e CJDBCS M , no contexto 75.
(b) SetupT
(a) ExecT
CJDBCI/CJDBCSM
0.35
(c) Total
CJDBCI/CJDBCSM
1.15
35 linhas/thread
65 linhas/thread
90 linhas/thread
CJDBCI/CJDBCSM
0.4
35 linhas/thread
65 linhas/thread
90 linhas/thread
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.35
0.3
1.1
0.3
0.25
1.05
0.25
0.2
0.2
1
0.15
0.15
0.95
0.1
0.1
0.05
0.9
10
30
50
70
90
0.05
10
quantidade de threads
30
50
70
90
10
30
quantidade de threads
50
70
90
quantidade de threads
Também aqui os resultados são semelhantes ao contextos anteriores.
Nota-se, porém, uma ligeira melhoria do desempenho do CJDBCS M para os valores de
número de linhas por thread maiores.
5.4.5
5.4.5.1
Fetch size 100%
CJDBCS M
A Figura 5.45 apresenta os resultados da comparação do desempenho de CJDBCI e de
CJDBCS M , no contexto Fetch size 100%.
O seguinte aplica-se a todos aos tempos de execução e total; o CJDBCS M para o número
de linhas por thread mais baixo, praticamente iguala o desempenho de CJDBCI , começando
a perder terreno à medida que os valores vão aumentando.
Em relação ao tempo de preparação, para o valores mais altos de linhas existe alguma
vantagem para o CJDBCI , e para o restante valor o desempenho é aproximadamente igual
para ambos.
72
Figura 5.45: Comparação entre CJDBCI e CJDBCS M , no contexto 100.
(b) SetupT
(a) ExecT
CJDBCI/CJDBCSM
1.15
CJDBCI/CJDBCSM
1.15
1.9
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.1
(c) Total
CJDBCI/CJDBCSM
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.8
1.1
1.7
1.05
1.05
1.6
1
35 linhas/thread
65 linhas/thread
90 linhas/thread
1
1.5
0.95
0.95
1.4
0.9
0.9
1.3
0.85
0.85
1.2
0.8
0.8
1.1
0.75
0.75
1
0.7
10
30
50
70
900.9
quantidade de threads
5.4.5.2
0.7
10
30
50
quantidade de threads
70
10
90
30
50
70
90
quantidade de threads
CJDBCS
A Figura 5.46 apresenta os resultados da comparação do desempenho de CJDBCI e de
CJDBCS , no contexto Fetch size 100%.
Figura 5.46: Comparação entre CJDBCI e CJDBCS , no contexto 100.
(a) ExecT
(b) SetupT
0.75
20
35 linhas/thread
65 linhas/thread
90 linhas/thread
18
(c) Total
CJDBCI/CJDBCS
CJDBCI/CJDBCS
CJDBCI/CJDBCS
12
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.7
35 linhas/thread
65 linhas/thread
90 linhas/thread
11
0.65
10
16
0.6
9
14
0.55
8
12
0.5
7
0.45
6
10
0.4
8
5
0.35
4
0.3
6
3
0.25
4
10
30
50
quantidade de threads
70
90
10
30
50
quantidade de threads
70
90 2
10
30
50
70
90
quantidade de threads
O desempenho geral de CJDBCS é muito superior ao de CJDBCI .
Verifica-se que um número de linhas por thread mais baixo, faz com que o crescimento
do desempenho relativo ao tempo de execução de CJDBCS face ao de CJDBCI seja mais
acentuado, enquanto que para um número de linhas maior o crescimento é quase horizontal.
O tempo de preparação do CJDBCI é bem melhor do que o de CJDBCS .
Apesar disso, no tempo total o CJDBCS tem muito melhor desempenho.
73
5.4.6
Resumo
Resumindo os resultados apresentados:
• O CJDBCI apresenta muito melhor desempenho do que CJDBCS M para todos os
contextos à excepção de 100, em que se encontram mais próximos, mas mantendo-se
superior;
• O CJDBCS é no mı́nimo 2 vezes mais rápido do que CJDBCI para o contexto 100,
e chega a ser 11.5 vezes mais rápido.
74
Capı́tulo 6
Discussão
6.1
Análise de resultados
Nas subsecções seguintes são discutidos e analisados os resultados obtidos e apresentados
no capı́tulo anterior.
6.1.1
Comparação com JDBC
Esta subsecção discute e analisa os resultados apresentados na secção 5.2.
Como já foi mencionado no capı́tulo dos resultados, todas as soluções apresentadas demonstraram um tempo de preparação muito melhor do que a solução M SJDBC. Este era o resultado esperado, na medida em que nesta solução tem que se criar uma statement e um result
set para para thread, enquanto que as restantes soluções possibilitam a partilha do mesmo
objecto result set. Isto significa que num ambiente multithreaded com n threads em execução
e em que o tempo de criação de um result set é dado por cRS , o tempo total do tempo de
preparação para M SJDBC será de n × cRS , enquanto que em CJDBC e em W JDBC será
de apenas cRS . Os resultados práticos comprovam esta teoria na medida em que o rácio entre
os tempos de preparação do M SJDBC e as outras soluções é numericamente aproximado
à quantidade de threads utilizado para efectuar a medição (veja-se por exemplo a Figura
5.12). Esta relação não se verifica para a solução CJDBCS porque esta é a única que na
preparação carrega os dados do result set para o cache. As outras soluções apenas criam o
result set (declaração e abertura do cursor do servidor). A vantagem do melhor tempo de
preparação revelou-se importante em diversas situações, porque onde por vezes existia uma
ligeira vantagem do M SJDBC, esta foi anulada com a ajuda do tempo de preparação. Por
exemplo na comparação entre M SJDBC e CJDBCI para a Actualização (fig. 5.1), que
para quantidades de threads e valores de número de linhas maiores o M SJDBC era cerca de
10% mais rápido, no tempo total a vantagem vai para o CJDBCI sendo cerca de 30% mais
rápido do que M SJDBC.
A solução CJDBCI revelou-se mais eficiente do que o M SJDBC em todos os contextos, com destaque para os contextos de modificação (Actualização, Inserção e Remoção),
75
em que a tendência é haver mais vantagem para o CJDBCI quantos mais threads estiverem em execução. Já no contexto da Leitura, embora exista uma clara vantagem para o
CJDBCI , a tendência não se verifica pois existe um ligeiro declı́nio em favor do desempenho
do M SJDBC. Esta situação justifica-se pelo facto de o cursor com cache individual, do
ponto de vista do cliente, ter um peso semelhante ao M SJDBC pois cada thread possui em
cache a totalidade do dataset. Depois como se pode ver no tempo de execução da Leitura
(fig. 5.4), a implementação da Microsoft é simplesmente mais eficiente do que a realizada
neste trabalho. No entanto é importante ressalvar que mesmo com a tendência para diminuir
o desempenho do CJDBCI face ao M SJDBC com o aumento da quantidade de threads, é
na Leitura que o CJDBCI demonstra maior superioridade sendo cerca de 35% a 70% mais
rápido do que M SJDBC. No contexto da inserção verificou-se muito equilı́brio, principalmente nos tempos de execução em que os resultados de M SJDBC e CJDBCI são muito
próximos. No tempo total consegue-se notar uma pequena superioridade de CJDBCI devido
ao bom desempenho do tempo de preparação. Uma outra nota vai para o facto de os resultados para os contextos da Actualização e da Remoção serem semelhantes. É compreensı́vel
que tal se suceda pois tirando o diferente valor para optype do RPC sp cursor (ver C.1), a
implementação dos métodos de actualização e remoção semelhantes. Ainda assim no contexto
da remoção não há actualização de valores das colunas pelo que o desempenho do CJDBCI
neste contexto é melhor do que na actualização.
A solução CJDBCS também apresenta um desempenho claramente superior ao desempenho de M SJDBC, melhor até porque apesar de não apresentar um tempo de preparação
tão bom, ao nı́vel da execução é mais eficiente. O tempo de preparação não é tão bom,
porque ao contrário do M SJDBC e do CJDBCI , o CJDBCS constrói o cache no momento
da criação dos cursores, refletindo-se o peso dessa operação no tempo de preparação em vez
do tempo de execução. É também por esta razão que apresenta melhores resultados para a
execução, pois uma vez que já tem os dados em cache não necessita de requisitar as linhas
do dataset, operação essa que se revela bastante penosa para o desempenho. Este aspecto é
claramente visı́vel para o contexto da Leitura em que o CJDBCS consegue ser no mı́nimo 4
vezes mais rápido do que M SJDBC e no máximo quase 18 vezes, e para além disso o desempenho comparativo melhora à medida que a quantidade de threads aumenta, ao contrário do
CJDBCI cujo desempenho comparativo diminui nessa situação (ver Figura 6.1).
Queria aqui referir um aspecto em relação ao contexto de Actualização. Como se pode ver
na Figura 6.2 o desempenho comparativo do CJDBCS em relação ao M SJDBC é melhor
do que o do CJDBCI . Mas porquê? Até se poderia pensar que aconteceria precisamente o
contrário, pois o cursor de cache partilhado tem de obter acesso exclusivo ao cache para o
actualizar, enquanto que o cache individual pode actualizar o seu cache sem essa necessidade.
A razão surge do facto que o cache individual tem a necessidade de mover o cursor do servidor
antes de efectuar a actualização, já o mesmo não acontece com o cursor com cache partilhado.
A actualização de um result set no SQL Server a partir de um cursor só é possı́vel ao fim
de se carregar (fetch) algumas linhas no buffer do cursor1 , e é mesmo lançado um erro caso o
76
Figura 6.1: Comparação entre CJDBC e M SJDBC, no contexto Leitura
MSJDBC/CJDBCI
0.98
MSJDBC/CJDBCS
20
35 linhas/thread
65 linhas/thread
90 linhas/thread
0.96
35 linhas/thread
65 linhas/thread
90 linhas/thread
18
0.94
16
0.92
14
0.9
12
0.88
10
0.86
0.84
8
0.82
6
0.8
4
0.78
10
30
50
70
90 2
quantidade de threads
10
30
50
quantidade de threads
70
90
Figura 6.2: Comparação entre CJDBC e M SJDBC, no contexto Actualização
MSJDBC/CJDBCI
1.06
MSJDBC/CJDBCS
1.5
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.04
35 linhas/thread
65 linhas/thread
90 linhas/thread
1.45
1.4
1.02
1.35
1
1.3
0.98
1.25
0.96
1.2
0.94
1.15
0.92
1.1
0.9
1.05
0.88
10
30
50
70
90
quantidade de threads
1
10
30
50
quantidade de threads
70
90
buffer esteja vazio. Na operação de fetch o servidor envia as linhas do buffer ao cliente, que
depois cria o seu próprio cache. O ı́ndice para actualizar uma linha do result set é relativa a
esse buffer e não ao result set. Isso quer dizer que o cliente pode pedir para actualizar, por
exemplo, a linha 3 e estar a actualizar a linha 60 do result set.
O cursor com cache individual não pode assumir que tem todas as linhas do result set,
logo assume que a sua noção de cache é diferente da noção do servidor, e por essa razão move
o cursor do servidor antes de actualizar, através de um UPDATE ABSOLUTE em vez de um
UPDATE normal. A diferença é que no UPDATE, o cursor utiliza o seu buffer para actualizar
os dados da tabela, enquanto que no UPDATE ABSOLUTE a modificação é realizada nas
tabelas. Já no cursor com cache partilhado os ı́ndices utilizados para identificar as linhas são
os mesmos nos dois lados e inalteráveis, pelo que se pode actualizar directamente usando o
cursor servidor.
Tal como se havia verificado com CJDBCI , o desempenho de CJDBCS e M SJDBC
1
Na documentação do protocolo é utilizada a designação de buffer em vez de cache.
77
para a Inserção, são próximos.
A solução WJDBC também revelou melhor desempenho do que M SJDBC em todos
os contextos, voltando a revelar uma relação directa entre o número de threads e o ganho
de desempenho para o tempo de preparação, tal como se verificou com CJDBCI . Ao nı́vel
da preparação as soluções W JDBC e CJDBCI são semelhantes na medida em que ambas
essencialmente o que fazem é abrir e alocar um cursor do servidor que por sua vez cria um
result set.
Nos contextos da Actualização e da Remoção os resultados foram semelhantes, porque tal
como foi explicado para CJDBCI as duas operações têm uma implementação semelhante.
No entanto, W JDBC tem maior vantagem sobre M SJDBC do que tem CJDBCI , mas isto
é um assunto a discutir na comparação com W JDBC na secção 6.1.2.
A operação em que W JDBC mais se destacou foi a Leitura. Para uma quantidade
reduzida de threads a vantagem é pequena, mas para quantidades maiores o desempenho
de W JDBC é muito superior ao de M SJDBC, chegando a ser 10 mais rápido. A razão
principal para este resultado tem a ver com as n cópias do result set que o M SJDBC tem
que efectuar do result set. Na preparação o M SJDBC tem que criar n vezes mais cursores,
e como é na execução que as linhas são realmente carregadas em cache, na execução têm
que ser carregados n vezes mais result sets. O resultado final é a grande superioridade de
desempenho do W JDBC sobre o M SJDBC.
Mais uma vez verifica-se que para o contexto da Inserção existe um maior equilı́brio nos
resultados não existindo uma vantagem clara para alguma das soluções.
6.1.2
Comparação com WJDBC
No geral a solução CJDBCI tem um desempenho inferior ao demonstrado pela solução
W JDBC.
Também na generalidade, para quantidades de threads maiores a solução CJDBCI perde
desempenho em relação a W JDBC. Embora a solução CJDBCI seja menos restritivo em
relação ao lock e permita que mais código seja executado concorrentemente, a solução é mais
pesada quanto à utilização de recursos do lado do cliente. A solução W JDBC partilha
o mesmo objecto result set utilizando cursores cujo peso é insignificante. Um cursor no
W JDBC não é muito mais do que uma classe que possui uma referência para um ResultSet
e um inteiro que guarda a linha actual do result set. Já a solução CJDBCI constrói um
cache por cursor. Com a excepção da alocação de um cursor do servidor, isso é semelhante ao
M SJDBC em que se constrói n result sets para n threads. À medida em que a quantidade
de threads aumenta é cada vez mais penoso criar um cache para cada cursor de cada thread,
o que se reflete numa perda de desempenho. É na Leitura que se nota mais o peso de criar
os vários caches.
A situação onde existe mais equilı́brio é na inserção, existindo até uma ligeira vantagem
para CJDBCI que ronda os 2 a 6% para quantidades de threads maiores. Na inserção a
78
interacção com o servidor é mı́nima, não existe o fetch do result set, apenas são inseridos
dados do cliente no servidor. O que se verifica no W JDBC para quantidades de threads
maiores é que existe menos código concorrente a ser executado, pois a solução W JDBC
bloqueia o acesso ao objecto result set quando inicia o processo de inserção. Daı́ surgir a
vantagem para o CJDBCI .
A solução CJDBCS e a W JDBC em geral têm um desempenho semelhante, o que até
era espectável pois o princı́pio de funcionamento de ambos é o mesmo: existe um objecto
utilizado para operar sobre o dataset e que é partilhado pelos threads.
A diferença encontra-se no local onde é realizado o acesso exclusivo ao objecto partilhado:
num nı́vel mais alto para o W JDBC, e num nı́vel mais baixo para o CJDBCS . Obtendo
o lock num nı́vel mais alto não há tanta sensibilidade para o distinguir o código que é concorrente do que não é concorrente; todo o código executado depois do lock é executado não
concorrentemente, e por isso pode acontecer estar-se a diminuir o desempenho. Obtendo o
lock num nı́vel mais baixo há a possibilidade de se identificarem as zonas do código que têm
de ser executadas com acesso exclusivo e que não podem ser executadas concorrentemente
(zona crı́tica), efectuando apenas o lock nessas zonas existe mais código concorrente. E com
mais código concorrente obtemos um aumento de desempenho.
Apesar da proximidade de desempenho de ambas as soluções, nota-se uma vantagem em
favor da solução CJDBCS . E é no contexto da Leitura que se verifica uma clara superioridade
de CJDBCS em relação a W JDBC. A razão está relacionada com o nı́vel mais baixo de
implementação dos locks e com a construção especial desta solução, em que se sabe que o
cache tem todas as linhas do dataset, e por isso comunica menos com o servidor, aumentando
o desempenho.
O tempo de preparação do CJDBCS é mais alto porque esta solução cria o cache partilhado na instanciação, e o W JDBC só pede as linhas do dataset quando precisa delas, ou seja,
em execução (o que também reduz o desempenho de W JDBC na execução, principalmente
no contexto da Leitura).
6.1.3
Comparação com atrasos
O efeito que a introdução de atraso provocou na comparação dos resultados entre as
soluções M SJDBC e CJDBC é quase inexistente, pelo que se pode dizer que a introdução de
atraso afecta as soluções na mesma proporção, levando à obtenção de resultados semelhantes
à situação de ausência de atraso.
Em relação à solução W JDBC notou-se uma perda desempenho enorme, resultado que já
era esperado principalmente na operação de actualização (e inserção também). O W JDBC
bloqueia o acesso ao objecto do result set quando começa o processo de actualização de uma
linha, e só o desbloqueia quando o processo termina. Com a introdução de atraso os threads
mantêm o lock durante mais tempo diminuindo a concorrência, e com o resultado prático de
79
diminuir muito o desempenho.
Ao contrário do que se pensava inicialmente, afinal a soma das partes é maior do que o
todo. Embora se introduza 8 vezes um atraso entre colunas, o atraso total verificado entre
linhas usando o valor 8 × atrasoC é bastante menor. A tı́tulo de curiosidade o tempo total
da realização do benchmark do atraso entre colunas foi de mais de 12h30 e o tempo total da
realização do benchmark do atraso entre linhas foi de cerca de 6h30. O facto de a introdução
de atraso entre colunas provocar mais atraso, também evidenciou mais diferenças entre os
resultados na ausência de atrasos e os resultados com atraso.
6.1.4
Cache individual vs Cache partilhado
Desde o inı́cio do trabalho previu-se que a implementação de cache partilhado viria sofrer
num contexto com múltiplos threads, pois numa situação em que os threads trabalhem em linhas diferentes o cache estaria continuamente a ser alterado. Este benchmark veio a confirmar
esse raciocı́nio. O CJDBCS M tem um desempenho inferior ao de CJDBCI .
Do fetch size de 10% para 20% nota-se uma perda de desempenho do CJDBCS . Isto
acontece porque com um cache maior, a sua actualização torna-se numa operação mais pesada,
e o maior número de linhas não chega para compensar a sua actualização. Para o fetch size
50% e 75% nota-se uma muito ligeira recuperação do CJDBCS M , e para 100% ambos
(CJDBCI e CJDBCS M ) têm desempenhos próximos para quantidades de threads mais
baixas. Este dado vem contribuir para a justificação da assunção de que a implementação do
cache partilhado deveria guardar todas as linhas no cache.
Existe mais um aspecto importante a referir.
Embora para o fetch size a 100% o
CJDBCS M se tenha aproximando do CJDBCI , ainda ficou aquém. Mas pelo que se tinha
visto na comparação com o M SJDBC, o CJDBCS tinha melhor desempenho do que o
CJDBCI . Isto aconteceu porque teve que ser criada uma nova versão do CJDBCS que
suportasse diferentes tamanhos de fetch size. A versão oficial da implementação do cache
partilhado está optimizada para tirar partido do facto de o cache conter todas as linhas, e
não ser necessário verificar se uma linha requisitada se encontra em cache. Nos resultados da
comparação da comparação entre CJDBCI e CJDBCS verifica-se uma grande superioridade
do CJDBCS , o que comprova que o cache partilhado deve conter a totalidade das linhas do
result set em cache.
6.2
Conclusão
Este trabalho provou que existem soluções que permitem um acesso concorrente aos
serviços da API JDBC, nomeadamente para acesso ao ResultSet. As soluções encontradas
não só fornecem um mecanismo que garante um estado correcto do ResultSet num ambiente
com múltiplos threads, como reduz a utilização e desperdı́cio de recursos no cliente e no
servidor.
O desempenho das soluções construı́das a pensar numa execução concorrente superou o
80
desempenho da solução que cria um result set para cada entidade concorrente (thread).
Face aos resultados de CJDBCS , CJDBCI e W JDBC conclui-se que se houver memória
disponı́vel no cliente para conter todo o result set, deve-se utilizar a solução CJDBCS ,
diminuindo assim a quantidade de tráfego de rede e diminuindo também a dependência sobre
os cursores do servidor, que se sabe serem menos eficientes do que utilizar um result set sem
cursor [16, 71]. Caso não seja possı́vel guardar na memória do cliente todo o result set, a
escolha da solução recai para o W JDBC se se pretender o máximo de desempenho. Porém,
a solução CJDBCI também garante um desempenho superior a JDBC.
Se tivermos em conta os resultados do benchmark que simula alguma actividade entre
operações sobre o ResultSet, então conclui-se que a solução W JDBC deve ser evitada pois
o seu desempenho sofreu um decréscimo enorme, apresentando um desempenho pior do que
JDBC.
6.3
Trabalho relacionado
O jTDS é um driver JDBC 3.0 open-source do tipo 4 para Microsoft SQL Server (6.5, 7,
2000, 2005 e 2008) e Sybase (10, 11, 12, 15) [68]. É baseado no projecto FreeTDS (implementação em C do protocolo TDS [6]) e implementa quase a totalidade da especificação 3.0 da
JDBC [67]. Quanto à concorrência suporta somente a execução concorrente de Statements.
A solução ResultSet Wrapper (W JDBC) teve como inspiração o trabalho realizado por
Óscar Narciso Mortágua Pereira, Rui Luı́s Andrade Aguiar e Maribel Yasmina Campos Alves
Santos, apresentado no artigo ”Assessment of a Enhanced ResultSet Component for Accessing
Relational Databases”[82]. A ideia principal é a mesma: encapsular um result set, controlando
o acesso concorrente a ele, através da criação de cursores cliente. Na solução desse artigo a
salvaguarda do contexto é realizada centralmente na entidade EResultSet. Basicamente ela
possui uma memória que é indexada pelo identificador do cursor, e que para cada posição
tem guardada a linha do result set para esse cursor. Se se verificar que o cursor activo foi
alterado, é realizada a salvaguarda do contexto do cursor anterior e restaurado o contexto
do novo. O W JDBC é diferente no sentido que a gestão do contexto é realizado de modo
distribuı́do, cabendo a cada cursor guardar o seu contexto, apenas a verificação de alteração
do cursor cliente é realizada centralmente. O W JDBC é assim uma implementação mais
flexı́vel e escalável, pois no EResultSet tem que ser definido um tamanho para a memória
que guarda os contextos.
6.4
Trabalho Futuro
Foi apenas implementada uma pequena porção da API JDBC, daı́ a utilização do driver
produzido tem que acontecer com restrições (por exemplo, só alguns tipos de dados SQL são
suportados). Por isso, de modo a permitir a utilização do driver num ambiente de produção,
devem ser adicionadas mais funcionalidades.
81
O trabalho apresentado neste documento está essencialmente centrado na implementação
concorrente da interface ResultSet. No entanto, a API JDBC possui muitas outras que podem
beneficiar de um estudo que leve a uma implementação concorrente. Por exemplo as interfaces
Statement e PreparedStatement.
Um outro aspecto em que se pode trabalhar no futuro é em melhorar a implementação
do TDS, e para isso seria importante saber as zonas crı́ticas ao nı́vel do desempenho. Para
descobrir essas zonas crı́ticas colocar-se-iam pontos de benchmark no funcionamento interno
do driver. Assim em vez de se saber quanto tempo demora a realizar uma tarefa complexa,
passa-se a ter a noção do tempo que cada unidade que a constitui leva a completar a sua
função. A avaliação das unidades permitiria melhorar a construção das que se revelassem
menos eficientes.
82
Glossário
ambiente multihreaded Aplicação que executa várias tarefas simultaneamente utilizando
threads separados para cada tarefa. 10, 20
Application Programming Interface (API) Conjunto de regras e especificações que estabelecem o modo como um software disponibiliza as suas funcionalidades. 1, 3
batch Conjunto de uma ou mais statements Transact-SQL enviadas ao SQL Server para
execução. 15, 19
boilerplate Este termo quando aplicado a código-fonte refere-se a código que pode ser reutilizado sem sofrer alterações. 28
bulk insert Método eficiente de preenchimento de uma tabela, invocado por um cliente num
servidor. 101
cache hit ratio Percentagem de acesso à cache em que o elemento procurado é lá encontrado. 28
classpath Lista com os directórios e ficheiros jar, utilizada pela Java Virtual Machine para
encontrar classes e pacotes Java. 5, 29
Data Manipulation Language Linguagem que define comandos para actualizar, inserir e
remover informação num base de dados. 16
database engine Serviço principal responsável pelas tarefas de armazenamento, gestão e
segurança dos dados [39]. 2, 3, 13
Database Management System (DBMS) Sistema que permite criar, gerir e utilizar uma
base de dados. 4, 5, 13
dataset Conjunto de dados, normalmente apresentados numa forma tabular. 1, 6, 28, 29,
33, 76, 79
fetch Pedido e carregamento de linhas de um dataset. 25, 27, 76, 79
garbage collector Thread que corre em background numa aplicação Java e que liberta a
memória de objecto que já não estejam a ser utilizados. 40
83
Java Virtual Machine Máquina virtual capaz de executar bytecode Java. 3, 4
lock Bloqueio do acesso a um objecto partilhado, permitindo acesso exclusivo à entidade
que mantêm o bloqueio. 22, 25, 27, 78, 79
Open Database Connectivity (ODBC) Interface de software standard para aceder a um
DBMS. 4
overhead Processamento adicional requirido para executar uma determinada tarefa. 29
override Reimplementação de um método de uma superclasse realizada por uma das suas
subclasses. 26, 28
query Pedido de informação a uma base de dados ou a um sistema de informação. 4
Relational Database Management System (DBMS) Sistema que permite criar, gerir
e utilizar uma base de dados relacional. 2, 3
Remote Procedure Call (RPC) Procedimento executado num sistema remoto. No âmbito das bases de dados significa a invocação de um stored procedure. 28, 76
ResultSet Interface Java do pacote java.sql que permite operar sobre o resultado da
execução de uma statement SQL. 13
statement SQL String com uma expressão numa linguagem que o servidor entende. 2, 6,
13, 16, 17, 19, 23, 25, 34
stored procedure Sub-rotina constituı́da por comandos T-SQL, disponı́vel num sistema de
base de dados relacional. 1, 101, 107
Tabular Data Stream Protocolo utilizado na comunicação entre a aplicação cliente e o
SQL Server. 1, 13, 14, 24, 115
User Defined Function (UDF) Função criada pelo utilizador que pode ser utilizada em
instruções SQL. 101
84
Acrónimos
API Application Programming Interface
CJDBC Designação genérica para a implementação concorrente do ResultSet
CJDBCI Designação para a solução concorrente do ResultSet com cache individual
CJDBCS Designação para a solução concorrente do ResultSet com cache partilhado
CLI Client Level Interface
DBMS Database Management System
JVM Java Virtual Machine
M SJDBC Designação para a solução que cria um ResultSet por thread
ODBC Open Database Connectivity
RBMS Relational Database Management System
RPC Remote Procedure Call
SQL Structured Query Language
TDS Tabular Data Stream
UDF User Defined Function
W JDBC Designação para a solução ResultSet Wrapper
85
86
Bibliografia
[1] Inc. Advanced Micro Devices.
chine environment.
Optimizing java performance in a virtual ma-
http://developer.amd.com/documentation/articles/pages/
optimizingjavainvmenvironment.aspx, 2009.
[2] Scott W. Ambler. The object-relational impedance mismatch. http://www.agiledata.
org/essays/impedanceMismatch.html, 2009.
[3] Malcolm P. Atkinson and O. Peter Buneman. Types and persistence in database programming languages. ACM Computing Surveys, 19:105–190, 1988.
[4] Malcolm P. Atkinson, Laurent Daynès, Mick J. Jordan, Tony Printezis, and Susan
Spence. An orthogonally persistent java, 1996.
[5] Malcolm P. Atkinson and Ronald Morrison. Orthogonally persistent object systems. The
Vldb Journal, 4:319–401, 1995.
[6] Brian Bruns. Freetds. http://www.freetds.org/, 2011. [Online; accessed May-2011].
[7] Jian Chen and Qiming Huang. Eliminating the impedance mismatch between relational
systems and object-oriented programming languages. In in Proce. the 6th International
Hong Kong Database Workshop, 1995.
[8] William R. Cook and Ali H. Ibrahim. Integrating programming languages & databases:
What’s the problem? 2005.
[9] Microsoft Corporation. Microsoft R SQL Server R 2008. http://www.microsoft.com/
sqlserver/2008/en/us/.
[10] Microsoft
Corporation.
Microsoft
SQL
Server
JDBC
Driver
3.0.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=
a737000d-68d0-4531-b65d-da0f2a735707, April 2010. [Online; accessed September2010].
[11] Microsoft Corporation. Tabular Data Stream Protocol Specification. http://msdn.
microsoft.com/en-us/library/dd304523(PROT.13).aspx, 2010.
November-2010].
87
[Online; accessed
[12] Microsoft Corporation.
All headers rule definition.
http://msdn.microsoft.com/
en-us/library/cc448573.aspx, 2011. [Online; accessed May-2011].
[13] Microsoft Corporation. Browse mode. http://msdn.microsoft.com/en-us/library/
aa936959(SQL.80).aspx, 2011. [Online; accessed May-2011].
[14] Microsoft Corporation. Building the connection url: Sql server 2008. http://msdn.
microsoft.com/pt-pt/library/ms378428.aspx, 2011. [Online; accessed May-2011].
[15] Microsoft Corporation.
Cursor Concurrency (Database Engine).
http://msdn.
microsoft.com/en-us/library/ms191493.aspx, 2011. [Online; accessed May-2011].
[16] Microsoft Corporation. Cursor implementations. http://msdn.microsoft.com/en-us/
library/ms189546(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[17] Microsoft Corporation.
Cursor stored procedures.
http://msdn.microsoft.com/
en-us/library/ms187801.aspx, 2011. [Online; accessed May-2011].
[18] Microsoft Corporation. Cursor types (database engine). http://msdn.microsoft.com/
en-us/library/ms188644(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[19] Microsoft Corporation. Cursors (Database Engine). http://msdn.microsoft.com/
en-us/library/ms191179(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[20] Microsoft Corporation. Cursors (Transact-SQL). http://msdn.microsoft.com/en-us/
library/ms181441(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[21] Microsoft Corporation.
Data types (transact-sql).
http://msdn.microsoft.com/
en-us/library/ms187752.aspx, 2011. [Online; accessed May-2011].
[22] Microsoft Corporation. Deprecated database engine features in sql server 2008 r2. http:
//msdn.microsoft.com/en-us/library/ms143729.aspx, 2011.
[23] Microsoft Corporation. Dynamic Cursors (Database Engine). http://msdn.microsoft.
com/en-us/library/ms189099(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[24] Microsoft Corporation. Fast Forward-only Cursors (Database Engine). http://msdn.
microsoft.com/en-us/library/ms187502(v=SQL.100).aspx, 2011. [Online; accessed
May-2011].
[25] Microsoft Corporation. Fetching and Scrolling. http://msdn.microsoft.com/en-us/
library/ms187881(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[26] Microsoft Corporation.
Forward-only Cursors (Database Engine).
http://msdn.
microsoft.com/en-us/library/ms178033(v=SQL.100).aspx, 2011. [Online; accessed
May-2011].
88
[27] Microsoft Corporation.
Keyset-driven Cursors (Database Engine).
http://msdn.
microsoft.com/en-us/library/ms179409.aspx, 2011. [Online; accessed May-2011].
[28] Microsoft Corporation.
Login7.
http://msdn.microsoft.com/en-us/library/
dd304019(v=PROT.13).aspx, 2011. [Online; accessed May-2011].
[29] Microsoft Corporation.
Nbcrow.
http://msdn.microsoft.com/en-us/library/
dd304783(v=PROT.13).aspx, 2011. [Online; accessed May-2011].
[30] Microsoft Corporation. Odbc–open database connectivity overview. http://support.
microsoft.com/kb/110093/en-us, 2011. [Online; accessed May-2011].
[31] Microsoft Corporation. Packet data token stream definition. http://msdn.microsoft.
com/en-us/library/dd340794(v=PROT.13).aspx, 2011. [Online; accessed May-2011].
[32] Microsoft Corporation. Packet header: Type. http://msdn.microsoft.com/en-us/
library/dd304214(v=PROT.13).aspx, 2011. [Online; accessed May-2011].
[33] Microsoft Corporation.
setresponsebuffering method (sqlserverstatement).
http://
msdn.microsoft.com/en-us/library/bb879939(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[34] Microsoft Corporation. Setting the connection properties: Sql server 2008. http://
msdn.microsoft.com/pt-pt/library/ms378988.aspx, 2011. [Online; accessed May2011].
[35] Microsoft Corporation. sp cursor (transact-sql). http://msdn.microsoft.com/en-us/
library/ff848759.aspx, 2011. [Online; accessed May-2011].
[36] Microsoft Corporation. sp cursorclose (transact-sql). http://msdn.microsoft.com/
en-us/library/ff848800.aspx, 2011. [Online; accessed May-2011].
[37] Microsoft Corporation. sp cursorfetch (transact-sql). http://msdn.microsoft.com/
en-us/library/ff848736.aspx, 2011. [Online; accessed May-2011].
[38] Microsoft Corporation. sp cursoropen (transact-sql). http://msdn.microsoft.com/
en-us/library/ff848737.aspx, 2011. [Online; accessed May-2011].
[39] Microsoft Corporation. Sql server database engine (sql server 2008). http://msdn.
microsoft.com/en-us/library/ms187875(v=SQL.100).aspx, 2011.
[40] Microsoft Corporation. SQLServerResultSet Members. http://msdn.microsoft.com/
en-us/library/ms378188(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[41] Microsoft Corporation. Sqlserverstatement class. http://msdn.microsoft.com/en-us/
library/ms378995(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
89
[42] Microsoft Corporation. Static cursors (database engine). http://msdn.microsoft.com/
en-us/library/ms191286.aspx, 2011. [Online; accessed May-2011].
[43] Microsoft Corporation. Understanding Cursor Types. http://msdn.microsoft.com/
en-us/library/ms378405(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[44] Microsoft Corporation. Using adaptive buffering. http://msdn.microsoft.com/en-us/
library/bb879937(v=SQL.100).aspx, 2011. [Online; accessed May-2011].
[45] Oracle Corporation. Pjama. http://labs.oracle.com/forest/opj.main.html, 2000.
[46] Oracle Corporation.
JSR-000221 JDBC 4.0.
http://jcp.org/aboutJava/
communityprocess/final/jsr221/index.html, 2006. [Online; accessed September2010].
[47] Oracle Corporation. Java Platform Standard Ed. 6 - Package java.sql. http://
download.oracle.com/javase/6/docs/api/java/sql/package-summary.html, 2010.
[Online; accessed April-2011].
[48] Oracle Corporation.
Types of JDBC technology drivers.
http://java.sun.com/
products/jdbc/driverdesc.html, 2010. [Online; accessed November-2010].
[49] Oracle Corporation. Class AtomicInteger. http://download.oracle.com/javase/1.
5.0/docs/api/java/util/concurrent/atomic/AtomicInteger.html, 2011. [Online;
accessed April-2011].
[50] Oracle Corporation. Class reentrantlock. http://download.oracle.com/javase/6/
docs/api/java/util/concurrent/locks/ReentrantLock.html, 2011.
[Online; ac-
cessed May-2011].
[51] Oracle Corporation. Interface connection. http://download.oracle.com/javase/6/
docs/api/java/sql/Connection.html, 2011. [Online; accessed May-2011].
[52] Oracle Corporation.
Jdbc overview.
http://www.oracle.com/technetwork/java/
overview-141217.html, 2011. [Online; accessed May-2011].
[53] Oracle Corporation.
veloper guides.
Jdk 6 java database connectivity (jdbc)-related apis & de-
http://download.oracle.com/javase/6/docs/technotes/guides/
jdbc/, 2011. [Online; accessed May-2011].
[54] Oracle Corporation. Package java.sql. http://download.oracle.com/javase/6/docs/
api/java/sql/package-summary.html, 2011. [Online; accessed May-2011].
[55] Oracle Corporation.
Package javax.sql.
http://download.oracle.com/javase/6/
docs/api/javax/sql/package-summary.html, 2011. [Online; accessed May-2011].
90
[56] Oracle Corporation.
System.gc() (java platform se 6).
http://download.oracle.
com/javase/6/docs/api/java/lang/System.html#gc(), 2011. [Online; accessed May2011].
[57] D. Crocker and P. Overell. [RFC] Augmented BNF for Syntax Specifications: ABNF.
http://www.ietf.org/rfc/rfc4234.txt, 2005. [Online; accessed November-2010].
[58] Maydene Fisher, Jon Ellis, and Jonathan Bruce. JDBC API Tutorial and Reference
(Third Edition). Addison Wesley, 2003.
[59] Jim Gray, editor. The Benchmark Handbook for Database and Transaction Systems (2nd
Edition). Morgan Kaufmann, 1993.
[60] Roedy Green.
Garbage collection: Java glossary.
http://mindprod.com/jgloss/
garbagecollection.html, 2011. [Online; accessed May-2011].
[61] Brian Göetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, and Doug Lea.
Java Concurrency In Practice. Addison-Wesley Professional, 2006.
[62] Qiming Huang and Jian Chen. Eliminating the impedance mismatch between relational
systems and object-oriented programming language. 1995.
[63] Sybase Inc.
TDS 5.0 Functional Specification.
http://www.sybase.com/content/
1040983/Sybase-tds38-102306.pdf, 2006. [Online; accessed November-2010].
[64] Wikimedia Foundation Inc. Tabular Data Stream. http://www.enotes.com/topic/
Tabular_Data_Stream, 2010. [Online; accessed November-2010].
[65] Wikipedia Foundation Inc. JDBC Driver. http://en.wikipedia.org/wiki/JDBC_
driver, 2010. [Online; accessed November-2010].
[66] The jTDS Project. System stored procedures (jtds documentation). http://jtds.
sourceforge.net/apiCursors.html. [Online; accessed December-2010].
[67] The jTDS Project. jtds feature matrix. http://jtds.sourceforge.net/features.
html, 2011. [Online; accessed May-2011].
[68] The jTDS Project. jTDS JDBC Driver. http://jtds.sourceforge.net/, 2011. [Online; accessed May-2011].
[69] Easysoft Limited. What is odbc? http://www.easysoft.com/developer/interfaces/
odbc/linux.html#what_is_odbc, 2011. [Online; accessed May-2011].
[70] Alonso Marquez, Stephen Blackburn, Gavin Mercer, and John N. Zigman. Implementing
orthogonally persistent java. In Workshop on Persistent Object Systems, pages 247–261,
2000.
91
[71] Brad
McGehee.
Performance
tuning
sql
server
sql-server-performance.com/tips/cursors_p1.aspx,
cursors.
http://www.
January 2007.
[Online;
accessed May-2011].
[72] Linda Null and Julia Lobur. The Essentials of Computer Organization and Architecture.
Jones and Bartlett Publishers, 2003.
[73] Scott Oaks and Henry Wong. Java Threads, Third Edition. O’Reilly Media, 2004.
[74] Onstrategies.com.
tional
databases.
Evans
data
rates
popularity
of
rela-
http://www.onstrategies.com/CURRENT-NEWS/
Evans-Data-Rates-Popularity-of-Relational-Databases.html, 2008.
[75] Mahmoud Parsian. JDBC Recipes: A Problem-Solution Approach (Problem-Solution
Approach). Apress, Berkely, CA, USA, 2005.
[76] David A. Patterson and John L. Hennessy. Computer Organization and Design: The
Hardware/Software Interface (Third Edition). Morgan Kaufmann, 2007.
[77] George Reese. Database Programming with JDBC & Java, Second Edition. O’Reilly
Media, 2001.
[78] Inc. Sybase. Embeddedsql. http://www.sybase.com/products/archivedproducts/
embeddedsql, 2011.
[79] ISO/IEC. Information technology. Database languages - sql - part 3: Call-level interface
(sql/cli). technical report 9075-3:1995. ISO/IEC, 1995.
[80] Inc. Unicode. The unicode consortium. http://unicode.org, 2011. [Online; accessed
December-2010].
[81] Robert Vieira. Professional SQL Server 2005 Programming. Wrox, 2006.
[82] Óscar Pereira, Rui Aguiar, and Maribel Santos. Assessment of a enhanced resultset
component for accessing relational databases. 2010.
92
Apêndice A
Estudo do SQLServerResultSet
Aquando da criação do objecto statement é definido o tipo de result set que é criado.
Esse tipo determina o modo como os dados são carregados do servidor; podem ser usados
cursores de servidor ou não, podem ser carregados todos os dados de uma só vez ou podem
ser carregados conforme a aplicação vai os vai requisitando.
Nesta secção é apresentado um estudo da classe SQLServerResultSet, que corresponde
à classe do driver da Microsoft que implementa a interface java.sql.ResultSet. O estudo
concentra-se no modo de interacção do ResultSet com o SQL Server.
A.1
Cursores no SQL Server
As operações numa base de dados relacional actuam sobre um conjunto de linhas que
satisfazem a cláusula WHERE de uma statement, no entanto muitas aplicações precisam de
trabalhar com blocos mais pequenos ou até com uma linha de cada vez. Os cursores fornecem
esse mecanismo, permitindo [19]:
• Posicionamento numa linha especı́fica.
• Acesso a uma linha ou um bloco de linhas a partir da localização actual no result set.
• Modificar (actualizar, remover) linhas.
• Diferentes nı́veis de visibilidade às modificações realizadas por outros no result set.
• Acesso ao result set a partir de Transact-SQL em scripts, stored procedures e triggers.
A.1.1
Fetching e Scrolling
A operação de obter uma linha a partir do cursor designa-se por fetch. Um cursor é
classificado quanto ao tipo de fetch que suporta [25]:
• Forward-only
As linhas são obtidas sequencialmente da primeira até à última.
93
• Scrollable
Qualquer linha pode ser obtida em qualquer direcção.
Um cursor Forward-only suporta a seguinte operação de fetch:
• FETCH NEXT
Obtém a próxima linha.
Um cursor Scrollalble suporta as seguintes operações de fetch:
• FETCH NEXT
Obtém a próxima linha.
• FETCH FIRST
Obtém a primeira linha.
• FETCH PRIOR
Obtém a linha anterior.
• FETCH LAST
Obtém a última linha.
• FETCH ABSOLUTE n
Obtém a linha n a partir da primeira linha.
• FETCH RELATIVE n
Obtém a linha n a partir da linha actual.
A.1.2
Concorrência
O SQL Server suporta 4 tipos de concorrência [15]:
• READ ONLY
A actualização usando o cursor não é permitida e não são obtidos locks nas linhas do
result set.
• OPTIMISTIC WITH VALUES
Não são obtidos locks nas linhas. Quando uma actualização ocorre os valores actuais
das colunas da linha são comparados com os valores anteriormente carregados, se forem
iguais procede-se à actualização, caso contrário é lançado um erro.
• OPTIMISTIC WITH ROW VERSIONING
A tabela a actualizar tem que possuir uma coluna do tipo timestamp. Numa actualização valores de timestamp são comparados para determinar se a linha foi alterada ou
não por outros, e em caso negativo os novos valores são guardados.
94
• SCROLL LOCKS
O cursor lê a linha obtendo um update lock. Se o cursor for aberto no decorrer de uma
transacção o lock mantém-se até ocorrer um commit ou um roll-back. Se o cursor for
aberto fora de uma transacção o lock da linha é liberto quando for obtida uma outra
linha.
A.1.3
Tipos de cursor
O SQL Server suporta os seguintes tipos de cursor [18]:
Forward-only [26]
Não suporta scrolling, as linhas são obtidas sequencialmente da primeira para a última.
As linhas só são carregadas quando são pedidas. As modificações provenientes das
operações de inserção, actualização e remoção, realizadas pelo próprio ou por outros
são visı́veis. O SQL Server implementa uma versão designada por Fast Forward-only,
com optimização de desempenho [24].
Static [42]
Quando o cursor é aberto é criada uma cópia do result set na base de dados tempdb.
Uma vez que trabalha com uma cópia, através deste tipo de cursor as modificações não
são visı́veis. Também é conhecido como cursor insensitive e cursor snapshot.
Keyset-driven [27]
São usadas chaves para aceder às linhas da tabela. A tabela tem que possuir uma
ou mais linhas que permitam identificar unicamente uma linha. O keyset (conjunto de
chaves) é criado como uma tabela na base de dados tempdb. As modificações, próprias
ou externas, nas colunas que não são chave são visı́veis, mas as inserções externas não
são visı́veis.
Dynamic [23]
Todas as modificações são visı́veis. Os valores e a ordem das linhas pode ser alterada
em cada fetch. Este tipo de cursor é o que tem mais baixo desempenho, principalmente
para maiores quantidades de dados, e têm também problemas de concorrência porque
em cada fetch o result set é reconstruı́do, e por isso em geral deve-se evitar a sua
utilização [81]. No entanto, para quantidades de dados pequenas o result set trabalha a
partir da RAM, senso nesta situação mais rápido do que o keyset, que trabalha a partir
do disco (este utiliza uma tabela temporária na tempdb) [81].
A.2
Tipos de cursor por result set
As caracterı́sticas requisitadas ao result set determinam a interacção que o SQLServerResultSet tem com o servidor. A Tabela A.1 mostra que tipo de cursor de servidor é criado para
cada caracterı́stica do result set [43]. A API JDBC permite definir os requisitos do result set
95
quanto à navegabilidade (forward-only ou scrollable) e à concorrência (actualizável ou só de
leitura), mas o SQLServerResultSet suporta ainda mais uma caracterı́stica, buffering, que é
indicada na coluna com o mesmo nome na Tabela A.1 e explicada na secção A.3.
Tabela A.1: Tipos de cursor suportados pelo driver
Tipo
Cursor
N/A
Caracterı́stica
Forward-only
read-only
Buffering
full
TYPE FORWARD ONLY
/
CONCUR READ ONLY
N/A
Forward-only
read-only
adaptive
TYPE FORWARD ONLY
/
CONCUR READ ONLY
Fast Forward
Forward-only
read-only
N/A
TYPE FORWARD ONLY
/
CONCUR UPDATABLE
Dynamic
Forwardonly
Forward-only
updatable
N/A
TYPE SCROLL INSENSITIVE
Static
Scrollable
read-only
N/A
TYPE SCROLL SENSITIVE
/
CONCUR READ ONLY
Keyset
Scrollable
read-only
N/A
TYPE SCROLL SENSITIVE
CONCUR UPDATABLE
/
CONCUR SS SCROLL LOCKS
CONCUR SS OPTIMISTIC CC
CONCUR SS OPTIMISTIC CCVAL
Keyset
Scrollable
updatable.
N/A
TYPE FORWARD ONLY
/
CONCUR READ ONLY
96
Descrição
Permite apenas uma passagem,
da primeira até à última linha,
pelo result set. Este é o comportamento por pré-definição.
O driver lê todo o result set
para a memória quando a
statement é executada.
Permite apenas uma passagem,
da primeira até à última linha,
pelo result set. O driver lê
as linhas do result set conforme vão sendo pedidas, minimizando a memória gasta pelo
cliente.
Permite apenas uma passagem,
da primeira até à última linha,
pelo result set usando o cursor do servidor. As linhas são
carregadas em blocos com o
tamanho fetch size.
Permite apenas uma passagem,
da primeira até à última linha,
pelo result set, permitindo
também a actualização das linhas. As linhas são carregadas
em blocos com o tamanho fetch
size.
O result set não é modificável,
e as modificações externas não
são visı́veis.
As linhas são
carregadas em blocos com o
tamanho fetch size.
As actualizações externas são
visı́veis, as remoções aparecem como dados inexistentes
e as inserções externas não
são visı́veis.
As linhas são
carregadas em blocos com o
tamanho fetch size.
As actualizações internas e externas são visı́veis, as remoções
aparecem como dados inexistentes e as inserções externas
não são visı́veis. As linhas são
carregadas em blocos com o
tamanho fetch size.
Tabela A.1: Tipos de cursor suportados pelo driver
Tipo
Cursor
N/A
Caracterı́stica
Forward-only
read-only
Buffering
full
or
adaptive
TYPE SS SERVER
CURSOR FORWARD ONLY
Fast Forward
Forward-only
N/A
TYPE SS SCROLL STATIC
Static
As
actualizações externas
não são reflectivdas.
N/A
TYPE SS SCROLL KEYSET
/
CONCUR READ ONLY
Keyset
Scrollable
read-only
N/A
TYPE SS SCROLL KEYSET
/
CONCUR UPDATABLE CONCUR SS SCROLL LOCKS
CONCUR SS OPTIMISTIC CC
CONCUR SS OPTIMISTIC CCVAL
Keyset
Scrollable
updatable.
N/A
TYPE SS SCROLL DYNAMIC
/
CONCUR READ ONLY
Dynamic
Scrollable
read-only
N/A
TYPE SS DIRECT
FORWARD ONLY
97
Descrição
Fornece um cursor no cliente
que não permite modificações
e cujos dados do result set podem ser todos carregados na
execução da statement. Não é
criado uma cursor no servidor.
Acede rapidamente todos os
dados usando um cursor no
servidor.
Permite modificações de for usado com
CONCUR UPDATABLE. As
linhas são carregadas em blocos com o tamanho fetch size.
É possı́vel usar adaptive buffering se o método setResponseBuffering da classe SQLServerStatement for explicitamente
invocado com o argumento
”adaptive”.
Esta opção é equivalente a
TYPE SCROLL INSENSITIVE. As linhas são carregadas
em blocos com o tamanho
fetch size.
As
actualizações
externas
são visı́veis, as remoções
aparecem como dados inexistentes e as inserções
externas não são visı́veis.
Esta opção é equivalente a
TYPE SCROLL SENSITIVE.
As linhas são carregadas em
blocos com o tamanho fetch
size.
As
actualizações
internas
e externas são visı́veis, as
remoções
aparecem
como
dados
inexistentes
e
as
inserções não são visı́veis.
Esta opção é equivalente a
TYPE SCROLL SENSITIVE.
As linhas são carregadas em
blocos com o tamanho fetch
size.
As actualizações e inserções
externas são visı́veis, e as
remoções aparecem como dados inexistentes. As linhas são
carregadas em blocos com o
tamanho fetch size.
Tabela A.1: Tipos de cursor suportados pelo driver
Tipo
TYPE SS SCROLL DYNAMIC
/
CONCUR UPDATABLE CONCUR SS SCROLL LOCKS
CONCUR SS OPTIMISTIC CC
CONCUR SS OPTIMISTIC CCVAL
Cursor
Dynamic
Caracterı́stica
Scrollable
updatable
Buffering
N/A
Descrição
As actualizações e inserções internas e externas são visı́veis,
e as remoções aparecem como
dados inexistentes. As linhas
são carregadas em blocos com
o tamanho fetch size.
O SQLServerResultSet para além de suportar os tipos de result set definidos pela interface
ResultSet, adiciona alguns tipos que permitem requisitar explicitamente tipos especı́ficos que
existem no SQL Server, tanto ao nı́vel da navegação (forward-only, scrollable), como ao nı́vel
do tipo de concorrência [40]:
• CONCUR SS OPTIMISTIC CC
Leitura e escrita com concorrência optimı́stica (row versioning) e sem locks de linha.
• CONCUR SS OPTIMISTIC CCVAL
Leitura e escrita com concorrência optimı́stica (values) e sem locks de linha.
• CONCUR SS SCROLL LOCKS
Leitura e escrita com concorrência optimı́stica e com locks de linha.
• TYPE SS DIRECT FORWARD ONLY
Cursor do tipo fast forward-only, só de leitura.
• TYPE SS SCROLL DYNAMIC
Cursor do tipo dynamic.
• TYPE SS SCROLL KEYSET
Cursor do tipo keyset.
• TYPE SS SCROLL STATIC
Cursor do tipo static.
• TYPE SS SERVER CURSOR FORWARD ONLY
Cursor do tipo fast forward-only, só de leitura.
A.3
Adaptive Buffering
O adaptive buffering é uma funcionalidade introduzida no Microsoft SQL Server 2005
JDBC Driver versão 1.2, que tem como finalidade o carregamento de grandes quantidades de
dados sem a necessidade de utilizar cursores do servidor [44].
O que isto significa é que existem dois modos de carregamento de dados: adaptive e
full. No modo adaptive é carregada a menor quantidade possı́vel de dados, enquanto que no
modo full todo o result set é lido do servidor em run time.
98
O acesso a esta funcionalidade é realiza pela utilização do método setResponseBuffering
[33] da classe SQLServerStatement [41], que recebe uma String (full ou adaptive) que indica
o modo desejado.
A classe SQLServerStatement é a implementação da interface java.sql.Statement.
A principal motivação é diminuir a quantidade de memória utilizada pela aplicação, e até
evitar situações em que a memória esgota e é lançado o erro OutOfMemoryError, quando
a aplicação JDBC trabalha com queries que produzem resultados muito grandes.
99
100
Apêndice B
Tabular Data Stream
O Tabular Data Stream (TDS) é um protocolo da camada aplicação utilizado para transferir informação entre um servidor de base de dados e um cliente [11, 63]. Foi desenhado
e desenvolvido em 1984 pela Sybase Inc. para ser utilizado no servidor SQL da empresa,
e mais tarde foi também desenvolvido pela Microsoft para ser utilizado no Microsoft SQL
Server [64].
B.1
Mensagens
Como qualquer protocolo de rede, o TDS efectua a comunicação usando troca de mensagens. Existem duas categorias de mensagens: mensagens do cliente e mensagens do servidor.
Resumidamente as mensagens do cliente são:
Pre-login
Handshake que tem de ocorrer antes do login, e que configura alguns parâmetros tais
como a encriptação.
Login
Mensagem que inicia o estabelecimento da comunicação com o servidor. Como resposta,
o servidor informa o cliente se aceitou ou rejeitou o pedido de comunicação.
SQL Command
Mensagem que na zona de dados contém um comando SQL ou batch de comandos SQL,
representado numa String codificada em Unicode [80].
SQL Command com Dados Binários
Mensagem que faz um pedido de execução de uma operação bulk insert usando um
comando SQL seguido de dados binários. O comando também é representado numa
String codificada em Unicode.
Remote Procedure Call (RPC)
Mensagem que faz um pedido de execução de um stored procedure ou uma UDF. A
mensagem contém o nome, opções e parâmetros do RPC.
101
Attention signal
Mensagem que cancela a execução de um comando.
Resumidamente as mensagens do servidor são:
Pre-login response
Resposta a uma mensagem de pre-login.
Login response
Resposta a uma mensagem de login. Contém informação sobre as caracterı́sticas do
servidor, informação opcional e mensagens de erro.
Row data
Resposta com os dados devolvidos pela execução de um comando. Esta mensagem é
precedida por uma descrição dos nomes das colunas e dos tipos de dados.
Return status
Resposta com o valor do estado de um RPC. Também é usada para enviar o estado do
resultado da execução de uma instrução T-SQL.
Return parameters
Resposta com os valores dos parâmetros de saı́da de um RPC.
Response completion
Resposta que indica o fim de um conjunto de resultados.
Error e Info
Resposta que transmite mensagens de erro ou mensagens informativas.
Attention Acknowledgment
Resposta que confirma a recepção de um cancelamento de execução de um comando.
B.2
Pacotes
Cada mensagem é constituı́da por um ou mais pacotes. Cada pacote tem um tamanho
máximo cujo valor é determinado na mensagem de login. Todos os pacotes da mensagem
excepto o último têm de ter um tamanho igual ao valor do tamanho máximo negociado.
Cada pacote é constituı́do por um cabeçalho (packet header ) e por uma zona de dados (packet
data).
B.2.1
Cabeçalho
102
Corresponde aos primeiros 8 bytes do pacote. Os campos do cabeçalho estão representados na seguinte tabela. O valor em cima de cada campo indica o número de bytes desse
campo.
Tabela B.1: Campos do cabeçalho TDS
1
1
2
2
1
1
Type
Status
Length
SPID
PacketId
Window
Descrição dos campos do cabeçalho:
Type
Define o tipo de mensagem.
Status
Indica o estado da mensagem (por exemplo, indica se a mensagem terminou).
Length
Indica o tamanho do pacote (incluindo o tamanho do cabeçalho).
SPID
Identifica o ID do processo no servidor correspondente à ligação actual. Este campo tem
carácter opcional, pelo que nesta implementação será sempre enviado o valor 0x0000.
PacketID
Indica o número do pacote. Cada pacote enviado incrementa este valor em uma unidade.
Window
Actualmente não é utilizado, por isso tem sempre o valor 0x00.
NOTA: Todos os valores são representados em network byte order (big-endian) e são
valores sem sinal.
B.2.2
Zona de dados
Todos os tipos de mensagens, exceptuando a Attention signal, a seguir ao cabeçalho têm
uma zona de dados [32].
NOTA: A zona de dados também pode ser denominada de data stream ou apenas stream.
Os pacotes tem um tamanho máximo, cujo valor é determinado no login. O tamanho do
pacote inclui o tamanho do cabeçalho.
Se uma mensagem produzir um pacote que ultrapasse o tamanho definido, terá de ser
dividida por múltiplos pacotes. Cada um desses pacotes terá um cabeçalho semelhante,
exceptuando os campos Status e Length. O campo Status terá o valor 0x0 se houverem mais
pacotes da mensagem e terá o valor 0x1 se o pacote é o último da mensagem. O campo
103
Length terá um valor igual ao tamanho definido, para todos os pacotes excepto para o último
da mensagem.
Existem dois tipos de zonas de dados: Token Stream e Tokenless Stream. Um token
stream é constituı́do por um ou mais tokens, em que cada um deles é seguidos pelos dados
relativos ao token. Um tokenless stream contém directamente os dados da mensagem, sem
recorrer a tokens para os descrever.
Na tabela a seguir temos um resumo das mensagens que usam tokens e as que não usam.
Tabela B.2: Indicação das mensagens que usam tokens
Mensagem
Origem
Token
Pre-Login
Cliente
Não
Login
Cliente
Não
SQL Batch
Cliente
Não
Bulk Load
Cliente
Sim
Remote Prodecure Call
Cliente
Sim
Attention
Cliente
Não
Transaction Manager Request
Cliente
Não
Pre-Login Response
Servidor
Não
Login Response
Servidor
Sim
Row Data
Servidor
Sim
Return Status
Servidor
Sim
Return Parameters
Servidor
Sim
Response Completion
Servidor
Sim
Error and Info Messages
Servidor
Sim
Attention Acknowledgment
Servidor
Não
A definição da gramática dos streams (token e tokenless) é especificada usando Augmented
Backus-Naur Form [57].
B.3
Tokenless Streams
Um tokenless stream contém directamente os dados da mensagem, sem recorrer a tokens
para os descrever.
A seguir é descrito o formato da zona de dados, das mensagens com tokenless streams.
B.3.1
Pre-Login
O stream desta mensagem é constituı́do por uma sequência de opções seguidas dos dados
relativos a essas opções. Cada opção tem três campos: Type, Position e Length. Type
104
identifica a opção, Position indica a posição que a opção ocupa nos dados e Length indica o
número de bytes que opção ocupa nos dados.
Tabela B.3: Opções da mensagem de Pre-Login
Opção
Valor
Descrição
VERSION
0x00
Versão do remetente. Normalmente usado para debugging.
ENCRYPTION
0x01
Negociar encriptação.
INSTOPT
0x02
Nome da instância do SQL Server.
THREADID
0x03
Id do thread da aplicação cliente. Usado para debugging.
TERMINATOR
0xFF
Assinala o fim da mensagem de Pre-Login.
Do que se conseguiu apurar apenas VERSION e ENCRYPTION são obrigatórios, e uma vez
que as restantes opções de momento são irrelevantes, os pacotes de Pre-Login do driver só
irão conter estas duas opções. Para além disso, ainda não será considerada a utilização de
encriptação.
B.3.2
Login
Este stream define as regras de autenticação entre o cliente e o servidor. O seu tamanho
não deve ultrapassar os 128-1 bytes.
A definição deste stream possui várias regras que podem ser consultadas em [28], das quais
se destacam a OffsetLength e a Data. Estas duas regras definem os parâmetros concretos do
login, tais como a base de dados a utilizar ou o nome de utilizador. A regra Data possui
os bytes que representam os dados dos parâmetros e a regra OffsetLength define a posição e
comprimento de cada parâmetro.
B.3.3
SQLBatch
Este stream define o formato de uma mensagem SQL Batch.
A definição deste stream é composta por uma regra ALL HEADERS1 seguida de um
stream em Unicode que contém o comando SQL.
B.4
Token Streams
As mensagens mais complexas (por exemplo, os dados do result set) são construı́das
usando tokens. Um token consiste num byte que funciona como identificador, seguido de
dados especı́ficos ao token.
Existem os seguintes tokens [31]:
1
Alguns streams TDS podem ser precedidos de vários cabeçalhos. A regra ALL HEADERS é utilizada
para especificar esses cabeçalhos[12].
105
Tabela B.4: Packet Data Token Streams
Nome
ALTMETADATA
ALTROW
COLINFO
COLMETADATA
DONE
DONEINPROC
DONEPROC
ENVCHANGE
ERROR
INFO
LOGINACK
NBCROW
OFFSET
ORDER
RETURNSTATUS
RETURNVALUE
ROW
SSPI
TABNAME
TVP ROW
Descrição
Descreve o tipo de dados, tamanho e nome da coluna que resulta
de uma SQL Statement que gera totais.
Usado para enviar uma linha com totais, cujo formato é descrito
pelo token ALTMETADATA.
Descreve a informação da coluna em Browse Mode [13],
sp cursoropen e sp cursorfetch.
Descreve o result set para interpretação dos tokens ROW que lhe
seguem.
Indica que uma SQL Statement foi terminada.
Indica que uma SQL Statement de um stored procedure foi terminada.
Indica que um stored procedure terminou.
Notificação de uma alteração de ambiente (por exemplo, base de
dados, lı́ngua).
Usado para enviar uma mensagem de erro ao cliente.
Usado para enviar uma mensagem de informação ao cliente.
Usado para enviar ao cliente a resposta a um pedido de login. A
ausência deste token numa resposta de login significa que o login
no servidor não foi realizado com sucesso.
Usado para enviar ao cliente uma linha definida pelo token COLMETADATA com compressão null bitmap (mais informações em
[29].
Usado para informar o cliente da posição onde uma palavra-chave
ocorre num SQL text buffer do próprio cliente. Este token foi
removido no TDS 7.2.
Usado para informar o cliente que colunas determinam a ordem
dos dados.
Usado para enviar ao cliente o valor do estado de um RPC.
Usado para enviar ao cliente o valor de retorno de um RPC.
Usado para enviar ao cliente uma linha completa, que foi anteriormente definida por um token COLMETADATA.
Token SSPI devolvido durante o processo de login.
Usado para enviar ao cliente o nome da tabela quando
sp cursoropen é utilizado ou quando em browser mode.
Usado para enviar uma linha table value parameter (TVP), do
cliente para o servidor.
106
Apêndice C
Cursor Stored Procedures
Existem instalados no SQL Server um conjunto de stored procedures que permitem a
operação de um cursor sobre um dataset [17, 66]. Este é um tema que não está directamente
relacionado com a descrição do protocolo TDS, mas do ponto de vista da implementação de
um driver JDBC, estes dois temas estão intimamente ligados.
A mensagem de rpc request do TDS possui um campo em que se pode indicar um número
de um stored procedure, esse stored procedure é um dos que se podem encontrar em [17] e
cujo número identificador se pode encontrar na respectiva documentação.
Estes stored procedures são o elemento fundamental na implementação de um result set
scrollable e/ou updatable.
Na Tabela C.1 são apresentados os stored procedures importantes para a implementação
do driver. A coluna procId corresponde ao identificador do stored procedure.
Tabela C.1: Stored prodecures do sistema relevantes para a implementação do driver JDBC.
procId
Nome
Descrição
1
sp cursor
Permite efectuar actualização, inserção ou remoção de
uma ou mais linhas do fetch buffer do cursor.
2
sp cursoropen
Abre um cursor definindo a statement SQL a ele associada e suas opções.
7
sp cursorfetch
Carrega uma ou mais linhas para o buffer do cursor.
Este buffer designa-se de fetch buffer.
9
sp cursorclose
Fecha e liberta os recursos associados ao cursor.
A seguir será apresentada e explicada a sintaxe de cada um dos stored procedures aqui
enunciados.
C.1
sp cursor
Mais informações sobre este stored procedure podem ser encontradas em [35].
107
C.1.1
Sintaxe
sp_cursor
cursor, optype, rownum, table
[ , value [...n] ] ]
C.1.2
Argumentos
cursor
Identificador do cursor gerado pelo SQL Server na execução do sp cursor.
optype
Identifica a operação a executar:
Valor Operação
Descrição
0x0001
UPDATE
Actualiza uma ou mais linhas indicadas por
rownum.
0x0002
DELETE
Remove uma ou mais linhas indicadas por
rownum.
0x0004
INSERT
Insere dados.
0x0008
REFRESH
Volta a preencher o fetch buffer com os dados
das tabelas.
0x10
LOCK
Provoca a aquisição de um SQL Server U-Lock
nas páginas que contêm a linhas especificadas.
0x20
SETPOSITION
Pode ser usado numa cláusula OR com REFRESH, UPDATE, DELETE ou LOCK para
mudar a posição do cursor para a última linha
modificada.
0x40
ABSOLUTE
Pode ser usado numa cláusula OR com UPDATE ou DELETE para modificar a linha indicada por rownum, e cujo valor é referente ao
data set criado pela SQL statement em vez de
se referir ao fetch buffer.
rownum
Especifica a linha do fetch buffer sobre a qual se irá realizar a operação.
table Nome da tabela sobre a qual será realizada a operação. Relevante quando a
statement SQL involve um join.
value String em Unicode que indica os valores de actualização/inserção.
C.2
sp cursoropen
Mais informações sobre este stored procedure podem ser encontradas em [38].
108
C.2.1
Sintaxe
sp_cursoropen cursor OUTPUT, stmt
[, scrollopt [ OUTPUT ] [ , ccopt [ OUTPUT ]
[ ,rowcount OUTPUT [ ,boundparam] [,...n] ] ] ] ]
C.2.2
Argumentos
cursor
Identificador do cursor gerado pelo SQL Server na execução do sp cursor.
stmt
SQL statement que define o result set do cursor.
scrollopt
Indica o tipo de cursor criado:
Valor
Descrição
0x0001
KEYSET
0x0002
DYNAMIC
0x0004
FORWARD ONLY
0x0008
STATIC
0x10
FAST FORWARD
0x1000
PARAMETERIZED STMT
0x2000
AUTO FETCH
0x4000
AUTO CLOSE
0x8000
CHECK ACCEPTED TYPES
0x10000
KEYSET ACCEPTABLE
0x20000
DYNAMIC ACCEPTABLE
0x40000
FORWARD ONLY ACCEPTABLE
0x80000
STATIC ACCEPTABLE
0x100000
FAST FORWARD ACCEPTABLE
ccopt
Indica o tipo de concorrência do cursor criado:
109
Valor
Descrição
0x0001
READ ONLY
0x0002
SCROLL LOCKS
0x0004
OPTIMISTIC
0x0008
OPTIMISTIC
0x2000
ALLOW DIRECT
0x4000
UPDT IN PLACE
0x8000
CHECK ACCEPTED OPTS
0x10000
READ ONLY ACCEPTABLE
0x20000
SCROLL LOCKS ACCEPTABLE
0x40000
OPTIMISTIC ACCEPTABLE
0x80000
OPTIMISITC ACCEPTABLE
rowcount Número de linhas do fetch buffer a ser usado pelo AUTO FETCH.
boundparam Significa o uso de parâmetros adicionais.
sp cursorfetch
C.3
Mais informações sobre este stored procedure podem ser encontradas em [37].
C.3.1
Sintaxe
sp_cursorfetch cursor
[ , fetchtype [ , rownum [ , nrows ] ] ]
C.3.2
Argumentos
cursor
Identificador do cursor gerado pelo SQL Server na execução do sp cursor.
fetchtype
Especifica que o buffer que deve ser carregado:
110
Valor
Nome
Descrição
0x0001
FIRST
Carrega o primeiro buffer de nrows linhas.
0x0002
NEXT
Carrega o próximo buffer de nrows linhas.
0x0004
PREV
Carrega o antecessor buffer de nrows linhas.
0x0008
LAST
Carrega o último buffer de nrows linhas.
0x10
ABSOLUTE
Carrega nrows linhas a partir da linha rownum.
0x20
RELATIVE
Carrega nrows linhas começando na linha
rownum.
0x80
REFRESH
Recarrega o buffer com os dados das tabelas.
0x100
INFO
Obtém informação acerca do cursor.
0x200
PREV NOADJUST
Usado como PREV, mas ao contrário de PREV,
esta opção não preenche o buffer com linhas que
se encontram na actual posição ou depois da actual posição do cursor.
0x400
SKIP UPDT CNCY
Quando usado os valores timestamp das colunas
não são escritos na tabela keyset quando uma
linhas é (re)carregada. Tem que ser utilizado
em conjunção com umas das outras opções à excepção de INFO.
rownum
Usado para especificar a linha de ABSOLUTE ou RELATIVE.
nrow
Especifica o número de linhas que devem ser carregadas pela operação. Por pré-definição
tem o valor 20.
sp cursorclose
C.4
Mais informações sobre este stored procedure podem ser encontradas em [36].
C.4.1
Sintaxe
sp_cursorclose cursor
C.4.2
Argumentos
cursor
Identificador do cursor gerado pelo SQL Server na execução do sp cursoropen.
111
112
Apêndice D
Funcionalidade implementada
Este anexo apresenta uma lista da funcionalidade da API JDBC implementada pelo driver.
Fica assim uma referência para a utilização do driver.
D.1
Implementação JDBC
As Tabelas D.1, D.2 e D.3 apresentam as interfaces do JDBC implementadas.
Tabela D.1: Métodos implementados da interface Driver
Nome
boolean
acceptsURL(String url)
Connection
connect(String url, Properties info)
int
getMajorVersion()
int
getMinorVersion()
boolean
jdbcCompliant()
Tabela D.2: Métodos implementados da interface Statement
Nome
void
addBatch(String sql)
void
clearBatch()
void
close()
int[]
executeBatch()
ResultSet
executeQuery(String sql)
int
executeUpdate(String sql)
113
Tabela D.3: Métodos implementados da interface ResultSet
Nome
boolean
absolute(int row)
void
afterLast()
void
beforeFirst()
void
cancelRowUpdates()
void
close()
void
deleteRow()
boolean
first()
Date
getDate(int columnIndex)
double
getDouble(int columnIndex)
int
getFetchSize()
int
getInt(int columnIndex)
int
getRow()
String
getString(int columnIndex)
void
insertRow()
boolean
isAfterLast()
boolean
isBeforeFirst()
boolean
isClosed()
boolean
isFirst()
boolean
isLast()
boolean
last()
void
moveToCurrentRow()
void
moveToInsertRow()
boolean
next()
boolean
previous()
void
refreshRow()
boolean
relative(int rows)
void
setFetchSize(int rows)
void
updateDate(int columnIndex, Date x)
void
updateDouble(int columnIndex, double x)
void
updateInt(int columnIndex, int x)
void
updateRow()
void
updateString(int columnIndex, String x)
boolean
wasNull()
114
D.2
Implementação TDS
O TDS especifica o formato como o SQL Server recebe e envia um valor, por isso o driver
tem de conhecer esse formato para conseguir comunicar com sucesso com o servidor. Como
existem vários tipos suportados pelo SQL Server [21] e o driver implementado apenas suporta
alguns, a Tabela D.4 apresenta a referência dos tipos suportados. Como o facto de uma coluna
permitir ou não valores nulos pode alterar o formato, a segunda coluna da Tabela D.4 indica
o suporte relativo a esta caracterı́stica.
Tabela D.4: Tipos SQL suportados pelo driver.
Nome
Null/Not Null
INT
S/S
FLOAT
S/S
DATETIME
S/S
NVARCHAR
S/S
115