Rede
Em primeiro lugar, como usar rowid e rownum é bloqueado pelo fornecedor de qualquer maneira, você deve considerar o uso de rotinas armazenadas no banco de dados. Isso pode reduzir significativamente a sobrecarga de transmissão de dados do banco de dados para o servidor de aplicativos (especialmente se eles estiverem em máquinas diferentes e conectados pela rede).
Considerando que você tem 80 milhões de registros para transmitir, esse pode ser o melhor aumento de desempenho para você, embora dependa do tipo de trabalho que seus threads fazem.
Obviamente, aumentar a largura de banda também ajudaria a resolver problemas de rede.
Desempenho do disco
Antes de fazer alterações no código, verifique a carga do disco rígido durante a execução das tarefas, talvez ele não consiga lidar com tanta E/S (10 threads lendo simultaneamente).
A migração para SSD/RAID ou banco de dados em cluster pode resolver o problema. Ao alterar a maneira como você acessa o banco de dados, não será nesse caso.
O multithreading pode resolver problemas de CPU, mas os bancos de dados dependem principalmente do sistema de disco.
Rownum
Existem alguns problemas que você pode enfrentar se estiver implementando-o usando rowid e rownum.
1) rownum é gerado dinamicamente para os resultados de cada consulta. Portanto, se a consulta não tiver classificação explícita e for possível que algum registro tenha um número de linha diferente toda vez que você executar a consulta.
Por exemplo, você o executa pela primeira vez e obtém resultados como este:
some_column | rownum
____________|________
A | 1
B | 2
C | 3
então você o executa pela segunda vez, já que você não tem classificação explícita, o dbms (por algum motivo conhecido por ele mesmo) decide retornar resultados como este:
some_column | rownum
____________|________
C | 1
A | 2
B | 3
2) o ponto 1 também implica que, se você estiver filtrando resultados em rownum ele irá gerar uma tabela temporária com ALL resultados e, em seguida, filtrá-lo
Então rownum não é uma boa opção para dividir resultados. Enquanto desordenado parecia melhor, tem alguns problemas também.
Robusto
Se você observar a descrição do ROWID você pode notar que "o valor de rowid identifica exclusivamente uma linha no banco de dados ".
Por causa disso e do fato de que quando você exclui uma linha você tem um "buraco" na sequência de rowid, os rowids podem não ser distribuídos igualmente entre os registros da tabela.
Então, por exemplo, se você tiver três threads e cada um buscar 1.000.000 rowids, é possível que um obtenha 1.000.000 registros e outros dois 1 registro cada. Assim, um ficará sobrecarregado, enquanto outros dois famintos .
Pode não ser grande coisa no seu caso, embora possa muito bem ser o problema que você está enfrentando atualmente com o padrão de chave primária.
Ou se você primeiro buscar todos os rowids no dispatcher e depois dividi-los igualmente (como o peter.petrov sugeriu) isso poderia fazer a coisa, embora buscar 80 milhões de ids ainda pareça muito, acho que seria melhor fazer a divisão com um sql-query que retorna bordas de pedaços.
Ou você pode resolver esse problema fornecendo uma pequena quantidade de rowids por tarefa e usando a estrutura Fork-Join introduzida no Java 7, no entanto, deve ser usado com cuidado .
Também ponto óbvio:rownum e rowid não são portáveis entre bancos de dados.
Portanto, é muito melhor ter sua própria coluna "sharding", mas você terá que se certificar de que ela divide os registros em partes mais ou menos iguais.
Lembre-se também de que, se você for fazer isso em vários threads, é importante verificar o que o banco de dados de modo de bloqueio usa , talvez ele apenas bloqueie a tabela para cada acesso, então o multithreading é inútil.
Como outros sugeriram, é melhor primeiro descobrir qual é o principal motivo do baixo desempenho (rede, disco, bloqueio de banco de dados, fome de encadeamento ou talvez você apenas tenha consultas abaixo do ideal - verifique os planos de consulta).