Придумал такое решение:
1. загружаем файл в память в виде массива байт, где каждые K-байт — это строка таблицы.
2. сортируем этот массив алгоритмом сортировки, который не требует доп. памяти, т.е. сортирует на месте или использует logN памяти. На практике выбрал модифицированный (для сортировки строк по K-байт) quicksort из Arrays.sort, он же описан в статье «Engineering a Sort Function». Ест logN памяти, а работает мега быстро (2000000 массив сортирует за 250ms).
3. делаем запрос select id,a,b,c from mytable order by id (грузим не сразу все, а пачками, используя fetchSize).
4. сейчас будем бежать по первому отсортированную массиву из файла, это будет индекс i, и по строкам из запроса, это будет индекс j. Для простоты объяснения проще представить, что есть два отсортированных массива.
5. i = 0, j = 0
5.1 если A[i].id == B[j].id, то (если A[i] != B[j], добавляем B[j] в temp_update); i++, j++;
5.2 если A[i].id > B[j].id, то B[j] добавляем в temp_insert; j++;
5.3 если A[i].id < B[j].id, то A[i] добавляем в temp_delete; i++;
6. выполняем 3 запроса к этим временным таблицам. таблица в базе синхронизирована
Итого: память O(1), а работать будет так же быстро, как и с map.
Если файл насколько большой, например, миллиард записей, то вначале сортируем таблицу в файле используя mergesort, а дальше переходим к пункту п3.