[JAVA] Vermeiden Sie den Datenbankzugriff von Schleifen so weit wie möglich

Wie der Titel schon sagt.

So was


//Hör auf damit
List<Person> people = new ArrayList<>();
for (int id : ids) {
    Person person = repository.findOne(p);
    people.add(person);
}

//Lass uns das machen
List<Person> people = repository.findAll(ids);

In meinem engen Beobachtungsbereich habe ich bereits SQL, um einen Fall abzurufen, daher sollte ich diesen verwenden, oder weil die Verarbeitungslogik einfacher ist, jeweils einen Fall abzurufen usw. ** [Verschieben] * Wenn es mit * erstellt wird, fällt es nicht so auf, und es ist ersichtlich, dass es später zu Leistungsproblemen führen wird.

Lassen Sie uns die Geschwindigkeit mit einem einfachen Beispielcode messen. Der Beispielcode lautet Spring Boot 2.0 + Spring Data JPA.

Schleifen Sie 10.000 Mal und rufen Sie den Prozess des Zugriffs auf die Datenbank aus der Schleife und des Abrufs von 10.000 Datensätzen sowie den Prozess des gleichzeitigen Abrufs von 10.000 Datensätzen mithilfe der IN-Klausel auf. Da es schwierig war, einen Bildschirm zu erstellen, habe ich ihn zu einem RestController gemacht und ihn entsprechend in JSON ausgegeben.

Verifizierungs-Schlüssel

Controller


package com.example.web;

import com.example.domain.service.BenchmarkService;
import com.example.domain.service.BenchmarkServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/benchmark")
@RequiredArgsConstructor
public class SandboxRestController {

    private final BenchmarkService benchmarkService;

    @RequestMapping("/sql/loop")
    public ResponseEntity<String> benchmarkDatabaseAccess() {
        //Jeweils 10 mit IN-Klausel,Holen Sie sich 000 Datensätze
        long beforeOneTime = System.currentTimeMillis();
        benchmarkService.oneTimesDatabaseAccess();
        long afterOneTime = System.currentTimeMillis();

        // 10,Holen Sie sich Datensätze nacheinander in 000 Schleifen
        long beforeLoopTime = System.currentTimeMillis();
        benchmarkService.tenThousandTimesDatabaseAccess();
        long afterLoopTime = System.currentTimeMillis();

        Map<String, Long> result = new HashMap<>();
        result.put("oneTimeFirst", afterOneTime - beforeOneTime);
        result.put("loopTimeFirst", afterLoopTime - beforeLoopTime);

        //Machen Sie den gleichen Vorgang erneut
        beforeOneTime = System.currentTimeMillis();
        benchmarkService.oneTimesDatabaseAccess();
        afterOneTime = System.currentTimeMillis();

        beforeLoopTime = System.currentTimeMillis();
        benchmarkService.tenThousandTimesDatabaseAccess();
        afterLoopTime = System.currentTimeMillis();

        result.put("oneTimeSecond", afterOneTime - beforeOneTime);
        result.put("loopTimeSecond", afterLoopTime - beforeLoopTime);
        return ResponseEntity.status(200).body(result.toString());
    }
}

Service


@Service
@RequiredArgsConstructor
public class BenchmarkServiceImpl implements BenchmarkService {

    private final BenchmarkRepository benchmarkRepository;

    public void tenThousandTimesDatabaseAccess() {
        for (int i = 1; i <= 10000; i++) {
            benchmarkRepository.findById(i);
        }
    }

    public void oneTimesDatabaseAccess() {
        List<Integer> ids = IntStream.range(1, 10000).boxed().collect(toList());
        benchmarkRepository.findByIdIn(ids);
    }
}

Verwenden Sie für die Datenbank die Tabelle, die von Spring Data JPA einfach erstellt wurde.

Entity


package com.example.domain.model;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

@Entity
@Data
@Table(name = "benchmark")
public class BenchmarkEntity {

    public BenchmarkEntity() {};

    public BenchmarkEntity(int id, int num) {
        this.id = id;
        this.num = num;
    }

    @Id
    @GeneratedValue
    private Integer id;

    @NotNull
    private int num;
}

Repository


package com.example.domain.repository;

import com.example.domain.model.BenchmarkEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface BenchmarkRepository extends JpaRepository<BenchmarkEntity, Integer> {

    // SELECT * FROM benchmark WHERE id = #{id}
    BenchmarkEntity findById(int id);

    // SELECT * FROM benchmark WHERE id IN (#{...ids})
    List<BenchmarkEntity> findByIdIn(List<Integer> ids);
}

Ergebnis

Als ich es mit h2database im Speicher betrieben habe

10.000 Schleifen erste 711 ms, zweite 438 ms Die Massenerfassung beträgt zum ersten Mal 393 ms und zum zweiten Mal 24 ms

Es war so ein Gefühl. Sogar der On-Memory-Speicher macht einen solchen Unterschied, sodass der Unterschied mit zunehmender Lesezeit pro Lesevorgang zunimmt, z. B. beim Zugriff auf eine Datenbank in einer anderen Umgebung.

Es ist einfacher zu verarbeiten, wenn Sie eine nach der anderen als Implementierung erfassen, und im Fall der Stapelerfassung kann die nachfolgende Verarbeitung etwas kompliziert sein, oder es kann einige Zeit dauern, bis eine Schleife in dieser Verarbeitung ausgeführt wird In den meisten Fällen sollte es schneller sein, alle auf einmal abzurufen (es sei denn, Sie geben SQL mit extrem schlechter Leistung aus).

Ich sage nicht, dass wir das aufgerufene SQL vollständig aus der Schleife entfernen sollten, aber seien Sie vorsichtig bei der Verarbeitung, die aufgrund der Anforderungen die Anzahl der Schleifen unter hoher Last erhöhen soll.

Referenz: https://github.com/tnemotox/sandbox

Recommended Posts

Vermeiden Sie den Datenbankzugriff von Schleifen so weit wie möglich
Vermeiden Sie Java Calendar so weit wie möglich
3. Erstellen Sie eine Datenbank für den Zugriff über das Webmodul