Skip to content

Commit

Permalink
[#235] 공간 인덱스 및 세컨더리 인덱스 적용 (#238)
Browse files Browse the repository at this point in the history
* refactor(P6spy): 정책에 따른 P6Spy 관련 설정 수정

* refactor(DB): SRID 수정

- SRID 를 0에서 5179로 변경
- 공간 인덱스를 설정하기 위함

* refactor(DB): 공간 인덱스 및 세컨더리 인덱스 추가

- 공간 인덱스 적용으로 성능 1만개 데이터 기준 6배 향상
- 유저 세컨더리 인덱스 적용으로 성능 1만개 기준 3배 향상

* feat: GeometryUtils 추가

- 동일한 SRID를 적용하기 위해

* feat: SRID 변경 코드 적용 및 인덱스 쿼리 추가

* feat: 데이터 제너레이터 및 로더 추가

- 데이터를 손쉽게 추가하기 위함

* fix: 유니크 제약으로 인한 테스트 수정

* fix: 유니크 제약으로 인한 테스트 수정
  • Loading branch information
JoosungKwon authored Aug 4, 2023
1 parent 75b474b commit 2624754
Show file tree
Hide file tree
Showing 28 changed files with 607 additions and 98 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ jacocoTestCoverageVerification {
}

excludes = [
'*.utils.*',
'*.dto.*',
'*.exception.*',
'*.scheduler.*',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public ResponseEntity<ApiResponse<CrewResponses>> getByUserId(
(
@ModelAttribute @Valid SearchCrewRequest distanceRequest
) {
CrewLocationResponses responses = crewService.getByLocation(distanceRequest);
CrewLocationResponses responses = crewService.getByLocationWithIndex(distanceRequest);
return ResponseEntity.ok().body(new ApiResponse<>(responses));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,17 @@ public interface CrewRepository extends JpaRepository<Crew, Long> {

@Query(nativeQuery = true, value =
"SELECT * FROM crew c "
+ "WHERE ST_DISTANCE_SPHERE(:location, c.location) < :distance AND c.status = 'RECRUITING'")
+ "WHERE ST_DISTANCE_SPHERE(:location, c.location) < :distance AND c.status = 'RECRUITING'")
List<Crew> findAllByLocation(@Param("location") Point location, @Param("distance") int distance);

@Query(nativeQuery = true, value = """
SELECT * FROM crew c
WHERE ST_Contains(
ST_Buffer(:location, :radius), c.location)
AND c.status = 'RECRUITING'
""")
List<Crew> findAllByLocation(@Param("location") Point location, @Param("radius") Double radius);

@Modifying
@Query(value = """
UPDATE Crew c
Expand All @@ -38,4 +46,6 @@ public interface CrewRepository extends JpaRepository<Crew, Long> {
""")
int updateAllStatusToFinish(@Param("time") LocalDateTime now);

@Query(value = "SELECT * FROM crew c ORDER BY RAND() LIMIT :count", nativeQuery = true)
List<Crew> findRandom(@Param("count") int count);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,7 @@ public interface CrewService {

CrewLocationResponses getByLocation(SearchCrewRequest distanceRequest);

CrewLocationResponses getByLocationWithIndex(SearchCrewRequest distanceRequest);

CrewStatusResponse updateStatus(Long crewId, Long userId, CrewStatus crewStatus);
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.prgrms.mukvengers.domain.user.model.User;
import com.prgrms.mukvengers.domain.user.repository.UserRepository;
import com.prgrms.mukvengers.global.common.dto.IdResponse;
import com.prgrms.mukvengers.global.utils.GeometryUtils;

import lombok.RequiredArgsConstructor;

Expand All @@ -60,6 +61,7 @@ public class CrewServiceImpl implements CrewService {
private final CrewMapper crewMapper;
private final StoreMapper storeMapper;
private final CrewMemberMapper crewMemberMapper;
private final GeometryFactory gf = GeometryUtils.getInstance();

@Override
@Transactional
Expand Down Expand Up @@ -157,8 +159,6 @@ public CrewPageResponse getByPlaceId(Long userId, String placeId, Pageable pagea
@Override
public CrewLocationResponses getByLocation(SearchCrewRequest distanceRequest) {

GeometryFactory gf = new GeometryFactory();

Point location = gf.createPoint(new Coordinate(distanceRequest.longitude(), distanceRequest.latitude()));

List<CrewLocationResponse> responses = crewRepository.findAllByLocation(location, distanceRequest.distance())
Expand All @@ -170,6 +170,23 @@ public CrewLocationResponses getByLocation(SearchCrewRequest distanceRequest) {
return new CrewLocationResponses(responses);
}

@Override
public CrewLocationResponses getByLocationWithIndex(SearchCrewRequest distanceRequest) {

Coordinate coordinate = new Coordinate(distanceRequest.longitude(), distanceRequest.latitude());

Point location = gf.createPoint(coordinate);
Double radius = GeometryUtils.calculateApproximateRadius(distanceRequest.distance());

List<CrewLocationResponse> responses = crewRepository.findAllByLocation(location, radius)
.stream()
.map(crew -> crewMapper.toCrewLocationResponse(crew.getLocation(), crew.getStore().getId(),
crew.getStore().getPlaceName()))
.toList();

return new CrewLocationResponses(responses);
}

@Override
@Transactional
public CrewStatusResponse updateStatus(Long crewId, Long userId, CrewStatus crewStatus) {
Expand Down Expand Up @@ -218,4 +235,5 @@ public CrewStatusResponse updateStatus(Long crewId, Long userId, CrewStatus crew

return new CrewStatusResponse(crew.getStatus());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.prgrms.mukvengers.domain.store.dto.request.CreateStoreRequest;
import com.prgrms.mukvengers.domain.store.dto.response.StoreResponse;
import com.prgrms.mukvengers.domain.store.model.Store;
import com.prgrms.mukvengers.global.utils.GeometryUtils;

@Mapper(componentModel = "spring")
public interface StoreMapper {
Expand All @@ -33,13 +34,10 @@ default Double mapLatitude(Store store) {

@Named("pointMethod")
default Point mapPoint(CreateStoreRequest request) {
GeometryFactory gf = GeometryUtils.getInstance();
Coordinate coordinate = new Coordinate(request.longitude(), request.latitude());

GeometryFactory gf = new GeometryFactory();

return gf.createPoint(new Coordinate(
request.longitude(),
request.latitude()));

return gf.createPoint(coordinate);
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.prgrms.mukvengers.domain.store.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.prgrms.mukvengers.domain.store.model.Store;
Expand All @@ -11,4 +13,6 @@ public interface StoreRepository extends JpaRepository<Store, Long> {

Optional<Store> findByPlaceId(@Param("placeId") String placeId);

@Query(value = "SELECT * FROM store s ORDER BY RAND() LIMIT :count", nativeQuery = true)
List<Store> findRandom(@Param("count") int count);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.prgrms.mukvengers.domain.user.repository;

import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -20,4 +21,6 @@ Optional<User> findByUserIdByProviderAndOauthId(
@Param("provider") String provider,
@Param("oauthId") String oauthId);

@Query(value = "SELECT * FROM users u ORDER BY RAND() LIMIT :count", nativeQuery = true)
List<User> findRandom(@Param("count") int count);
}
Original file line number Diff line number Diff line change
@@ -1,60 +1,16 @@
package com.prgrms.mukvengers.global.config.p6spy;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;

import javax.annotation.PostConstruct;

import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.springframework.context.annotation.Configuration;

import com.p6spy.engine.logging.Category;
import com.p6spy.engine.spy.P6SpyOptions;
import com.p6spy.engine.spy.appender.MessageFormattingStrategy;

@Configuration
public class P6spyConfig implements MessageFormattingStrategy {
public class P6spyConfig {

@PostConstruct
public void setLogMessageFormat() {
P6SpyOptions.getActiveInstance().setLogMessageFormat(this.getClass().getName());
}

@Override
public String formatMessage(int connectionId, String now, long elapsed, String category,
String prepared, String sql, String url) {
sql = formatSql(category, sql);
Date currentDate = new Date();

SimpleDateFormat formatter = new SimpleDateFormat("yy.MM.dd HH:mm:ss");

return category + " | " + "OperationTime : " + elapsed + "ms" + sql;
}

private String formatSql(String category, String sql) {
if (sql == null || sql.isBlank()) {
return sql;
}

if (Category.STATEMENT.getName().equals(category)) {
String tmpsql = sql.trim().toLowerCase(Locale.ROOT);
if (tmpsql.startsWith("create") || tmpsql.startsWith("alter")) {
sql = FormatStyle.DDL.getFormatter().format(sql);
} else {
sql = FormatStyle.BASIC.getFormatter().format(sql);
}
sql = "\n" + stackTrace() + "\n" + sql + "\n";
}

return sql;
P6SpyOptions.getActiveInstance().setLogMessageFormat(P6spyMessageFormatter.class.getName());
}

private String stackTrace() {
return Arrays.toString(Arrays.stream(new Throwable().getStackTrace())
.filter(t -> t.toString().startsWith("com.prgrms.mukvengers"))
.toArray()).replace(", ", "\n");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.prgrms.mukvengers.global.config.p6spy;

import java.util.Arrays;
import java.util.Locale;

import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import com.p6spy.engine.logging.Category;
import com.p6spy.engine.spy.appender.MessageFormattingStrategy;

@Profile("default")
@Configuration
public class P6spyMessageFormatter implements MessageFormattingStrategy {

@Override
public String formatMessage(int connectionId, String now, long elapsed, String category,
String prepared, String sql, String url) {

sql = formatSql(category, sql);
return category + " | " + "OperationTime : " + elapsed + "ms" + sql;
}

private String formatSql(String category, String sql) {
if (sql == null || sql.isBlank()) {
return sql;
}

if (Category.STATEMENT.getName().equals(category)) {
String tmpsql = sql.trim().toLowerCase(Locale.ROOT);
if (tmpsql.startsWith("create") || tmpsql.startsWith("alter")) {
sql = FormatStyle.DDL.getFormatter().format(sql);
} else {
sql = FormatStyle.BASIC.getFormatter().format(sql);
}
}

sql += "\n";

String[] stackTrace = stackTrace();

if (stackTrace.length > 0) {
sql += Arrays.toString(stackTrace).replace(", ", "\n");
}

return sql;
}

private String[] stackTrace() {
return Arrays.stream(new Throwable().getStackTrace())
.map(StackTraceElement::toString)
.filter(string -> string.startsWith("com.prgrms.mukvengers")
&& !string.startsWith("com.prgrms.mukvengers.global.config.p6spy")
&& !string.startsWith("com.prgrms.mukvengers.MukvengersApplication.main"))
.toArray(String[]::new);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.prgrms.mukvengers.global.utils;

import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.PrecisionModel;

public class GeometryUtils {

private static final GeometryFactory INSTANCE
= new GeometryFactory(new PrecisionModel(), 5179);

private GeometryUtils() {
/* no-op */
}

public static GeometryFactory getInstance() {
return INSTANCE;
}

// Meter -> radius
public static double calculateApproximateRadius(int distanceInMeters) {
// The approximate radius of the earth in meters
double earthRadius = 6371000;

// convert distance to radius in radians
double radiusInRadians = distanceInMeters / earthRadius;

// convert radians to degrees
return Math.toDegrees(radiusInRadians);
}

}
Loading

0 comments on commit 2624754

Please sign in to comment.