-
λ°μ΄ν°λ² μ΄μ€μμ λͺ¨λ νΈλμμ μ λν΄ Lockκ³Ό Unlockμ μ¬μ©νλ λ°©λ²μ λ°μ΄ν°λ² μ΄μ€ λ° νΈλμμ μ λμ λ°©μμ λ°λΌ λ€λ¦ λλ€. μΌλ°μ μΌλ‘ ν μ΄λΈ μ κΈ, ν μ κΈ, νΈλμμ 격리 μμ€, λͺ μμ μ κΈ ν΄μ λ₯Ό μ‘°ν©νμ¬ κ΅¬νν μ μμ΅λλ€. Java νκ²½μμλ JDBC, JPA, QueryDSL λ±μ νμ©ν΄ μ΄λ₯Ό μ€μ ν μ μμ΅λλ€.
μλλ SELECTλΏλ§ μλλΌ INSERT, UPDATE, DELETE λ± λͺ¨λ νΈλμμ μμ Lockκ³Ό Unlockμ μ¬μ©νλ λ°©λ²μ μ€λͺ ν©λλ€.
1. ν μ΄λΈ μ κΈ (Table Lock)
ν μ΄λΈ λ¨μλ‘ μ κΈμ μ€μ νμ¬ νΈλμμ μ΄ μλ£λκΈ° μ κΉμ§ λ€λ₯Έ νΈλμμ μ μ κ·Όμ λ§μ΅λλ€.
CreatedWed, 26 Mar 2025 11:33:45 +0900 -
QueryDSLμ μ¬μ©ν λ λ°μ΄ν°λ² μ΄μ€μ Lock κΈ°λ₯μ νμ©νλ λ°©λ²μ JPAμ μ μ¬ν©λλ€. QueryDSLμ JPAμ ν΅ν©λμ΄ λμνλ©°, JPQL λ° Native Queryμμ μ¬μ©νλ μ κΈ λ©μ»€λμ¦μ μ 곡ν©λλ€. μλλ QueryDSLμμ Lockκ³Ό κ΄λ ¨λ μ£Όμ κΈ°λ₯κ³Ό ꡬν λ°©λ²μ μ€λͺ ν©λλ€.
1. QueryDSLμμ Pessimistic Lock μ¬μ©
QueryDSLμ JPA
LockModeTypeμ μ§μνμ¬ λΉκ΄μ μ κΈμ ꡬνν μ μμ΅λλ€.μμ :
LockModeType.PESSIMISTIC_WRITEμ¬μ©import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; import jakarta.persistence.LockModeType; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class ProductService { private final JPAQueryFactory queryFactory; public ProductService(EntityManager entityManager) { this.queryFactory = new JPAQueryFactory(entityManager); } @Transactional public Product findByIdWithLock(Long productId) { QProduct product = QProduct.product; // Pessimistic Write Lock μ€μ return queryFactory.selectFrom(product) .where(product.id.eq(productId)) .setLockMode(LockModeType.PESSIMISTIC_WRITE) .fetchOne(); } }μ£Όμ ν¬μΈνΈ:
setLockMode(LockModeType.PESSIMISTIC_WRITE)λ₯Ό μ¬μ©νμ¬ λΉκ΄μ μ°κΈ° μ κΈμ μ€μ ν©λλ€.PESSIMISTIC_READμ κ°μ λ€λ₯Έ μ κΈ λͺ¨λλ μ¬μ©ν μ μμ΅λλ€.
2. λκ΄μ μ κΈ (Optimistic Lock)
λκ΄μ μ κΈμ QueryDSLμμ λ³λλ‘ μ§μλμ§ μμ§λ§, JPA μν°ν°μ
@Versionνλλ₯Ό νμ©νμ¬ λμν©λλ€.CreatedWed, 26 Mar 2025 11:30:45 +0900 -
Java μ ν리μΌμ΄μ μ μ€νν λ
-Xmx512mκ³Ό-XX:MaxRAM=512mλ λͺ¨λ μ΅λ λ©λͺ¨λ¦¬ μ¬μ©λμ μ ννλ μ΅μ μ΄μ§λ§, λμ λ°©μμ μ°¨μ΄κ° μμ΅λλ€.1.
-Xmx512m- ν(Heap) λ©λͺ¨λ¦¬μ μ΅λ ν¬κΈ°λ₯Ό 512MBλ‘ μ€μ ν©λλ€.
- μμ :
java -Xmx512m -jar myapp.jar - μ΄ μ€μ μ Java ν λ©λͺ¨λ¦¬λ§ μ ννλ©°, μ 체 νλ‘μΈμ€μ λ©λͺ¨λ¦¬ μ¬μ©λμ μ ννλ κ²μ μλλλ€.
- μ¦, ν μΈμ λ©λͺ¨λ¦¬(λ©νμ€νμ΄μ€, μ€ν, λ€μ΄ν°λΈ λ©λͺ¨λ¦¬ λ±)λ λ°λ‘ μ νλμ§ μμ΅λλ€.
2.
-XX:MaxRAM=512m- JVMμ΄ μ¬μ©ν μ μλ μ 체 λ©λͺ¨λ¦¬(ν + λ©νμ€νμ΄μ€ + κΈ°ν λ€μ΄ν°λΈ λ©λͺ¨λ¦¬) λ₯Ό 512MBλ‘ μ νν©λλ€.
- μμ :
java -XX:MaxRAM=512m -jar myapp.jar -XX:MaxRAMμ μ€μ νλ©΄-Xmxμ κΈ°λ³Έκ°μ΄ λ¬λΌμ§ μ μμ΅λλ€.- μΌλ°μ μΌλ‘
-Xmxμ κΈ°λ³Έκ°μMaxRAMκ°μ 25~50% μ λλ‘ μ€μ λ©λλ€(JVM λ²μ μ λ°λΌ λ€λ¦).
- μΌλ°μ μΌλ‘
- μ΄ μ€μ μ 컨ν μ΄λ νκ²½(Docker, Kubernetes) μμ JVMμ΄ μ¬μ©ν μ μλ λ©λͺ¨λ¦¬λ₯Ό μ μ΄νλ λ° μ μ©ν©λλ€.
μ°¨μ΄μ μμ½
μ΅μ λμ μ€λͺ -Xmx512mν(Heap) λ©λͺ¨λ¦¬ ν λ©λͺ¨λ¦¬μ μ΅λ ν¬κΈ°λ§ μ ν -XX:MaxRAM=512mμ 체 JVM λ©λͺ¨λ¦¬ ν + λ©νμ€νμ΄μ€ + λ€μ΄ν°λΈ λ©λͺ¨λ¦¬λ₯Ό ν¬ν¨ν JVM μ 체 λ©λͺ¨λ¦¬ μ ν ν¨κ» μ¬μ©νλ©΄?
λ μ΅μ μ ν¨κ» μ¬μ©ν μλ μμ΅λλ€.
CreatedWed, 26 Mar 2025 11:26:45 +0900 -
Javaμμ λ°μ΄ν°λ² μ΄μ€μ SELECT 쿼리μ λν΄ Lockκ³Ό Unlockμ μ¬μ©νλ λ°©λ²μ λ°μ΄ν°λ² μ΄μ€ μμ€ν κ³Ό 쿼리μ μ€μ μ λ°λΌ λ¬λΌμ§λλ€. μΌλ°μ μΌλ‘ SELECT μ체λ λ°μ΄ν°λ² μ΄μ€μμ μ κΈμ μ λ°νμ§ μμ§λ§, νΈλμμ μ΄λ 쿼리 ννΈλ₯Ό μ¬μ©νμ¬ μ κΈ λμμ μ μ΄ν μ μμ΅λλ€. μλμ λͺ κ°μ§ λ°©λ²μ μ€λͺ ν©λλ€.
1. νΈλμμ μ μ¬μ©ν Lock μ μ΄
νΈλμμ μ ν΅ν΄ SELECT λ¬Έμ΄ μ€ν μ€μΈ λμ λ€λ₯Έ μμ μ΄ λ°μ΄ν°μ μ κ·Όνμ§ λͺ»νλλ‘ μ κΈμ μ€μ ν μ μμ΅λλ€.
μμ μ½λ:
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; public class DatabaseLockExample { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/your_database"; String user = "your_username"; String password = "your_password"; try (Connection connection = DriverManager.getConnection(url, user, password)) { // Auto-commit λΉνμ±ν connection.setAutoCommit(false); // SELECT ... FOR UPDATE (ν μ κΈ μ€μ ) String sql = "SELECT * FROM your_table WHERE id = ? FOR UPDATE"; try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) { preparedStatement.setInt(1, 1); // ID κ° λ°μΈλ© ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { System.out.println("Data: " + resultSet.getString("your_column")); } // μμ ν μ»€λ° connection.commit(); } } catch (Exception e) { e.printStackTrace(); } } }μ€λͺ :
FOR UPDATEλ ν΄λΉ νμ μ κ·Έκ³ , λ€λ₯Έ νΈλμμ μ΄ μμ νμ§ λͺ»νλλ‘ ν©λλ€.- νΈλμμ μ 컀λ°νκ±°λ λ‘€λ°±νλ©΄ μ κΈμ΄ ν΄μ λ©λλ€.
2. 쿼리 ννΈλ₯Ό μ¬μ©ν Lock μ μ΄
λ°μ΄ν°λ² μ΄μ€μ λ°λΌ SELECT 쿼리μ ννΈλ₯Ό μΆκ°νμ¬ μ κΈ λμμ μ§μ ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, MySQLμμ
LOCK IN SHARE MODEλλFOR UPDATEλ₯Ό μ¬μ©ν μ μμ΅λλ€.CreatedWed, 26 Mar 2025 11:25:45 +0900 -
Spring Boot JPAμμ
@Transactionalμ¬μ©λ²Spring Boot JPAμμ
@Transactionalμ΄λ Έν μ΄μ μ νΈλμμ λ²μ λ΄μμ λ°μ΄ν°λ² μ΄μ€ μμ μ μ€ννκ³ , μ€ν¨ μ λ‘€λ°±νλ κΈ°λ₯μ μ 곡νλ μ€μν μμμ λλ€.
1.
@TransactionalκΈ°λ³Έ μ¬μ©λ²Springμ
@Transactionalμ μ¬μ©νλ©΄ νλμ λ©μλμμ μ¬λ¬ κ°μ JPA μμ (μ‘°ν, μ μ₯, μμ , μμ λ±)μ μνν λ νΈλμμ μ μλμΌλ‘ κ΄λ¦¬ν μ μμ΅λλ€.import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class MemberService { private final MemberRepository memberRepository; public MemberService(MemberRepository memberRepository) { this.memberRepository = memberRepository; } @Transactional public void updateMember(Long id, String name) { Member member = memberRepository.findById(id) .orElseThrow(() -> new IllegalArgumentException("Member not found")); member.setName(name); // JPA μμμ± μ»¨ν μ€νΈμ μν΄ μλμΌλ‘ μ λ°μ΄νΈλ¨ } }@Transactionalμ μ¬μ©νλ©΄, λ©μλ μ€ν μ€ μμΈκ° λ°μνλ©΄ λ‘€λ°±λλ©°, μ±κ³΅νλ©΄ μλμΌλ‘ 컀λ°λ©λλ€.member.setName(name);μ νΈμΆνλ©΄ JPAμ λ³κ²½ κ°μ§(dirty checking) λλΆμsave()λ₯Ό λͺ μμ μΌλ‘ νΈμΆνμ§ μμλ μ λ°μ΄νΈλ©λλ€.
2. μ½κΈ° μ μ© νΈλμμ (
@Transactional(readOnly = true))readOnly = trueλ₯Ό μ€μ νλ©΄ μ±λ₯ μ΅μ νλ₯Ό ν μ μμ΅λλ€.CreatedWed, 26 Mar 2025 11:21:45 +0900 -
Spring Boot JPAμμ @Transactional μ΅μ μ 리
Spring Boot JPAμμ
@Transactionalμ νΈλμμ μ²λ¦¬λ₯Ό μν΄ μ¬μ©νλ λ§€μ° μ€μν μ΄λ Έν μ΄μ μ΄μμ. λ°μ΄ν°λ² μ΄μ€μ μΌκ΄μ±κ³Ό 무결μ±μ μ μ§νκΈ° μν΄ μ¬λ¬ μμ μ νλμ νΈλμμ μΌλ‘ λ¬Άμ΄ μ€νν λ μ¬μ©νμ£ . μλμ μ¬μ©λ²μ μ 리ν΄λ³Όκ²μ.
π κΈ°λ³Έ μ¬μ©λ²
import org.springframework.transaction.annotation.Transactional; @Service public class MemberService { @Transactional public void joinMember(Member member) { memberRepository.save(member); // μμΈ λ°μ μ μ 체 νΈλμμ λ‘€λ°± } }@Transactionalμ λ©μλμ λΆμ΄λ©΄ ν΄λΉ λ©μλ μμ μμ μ΄ νλμ νΈλμμ μΌλ‘ μ²λ¦¬λΌμ.- λ°νμ μ€ μμΈκ° λ°μνλ©΄ μλμΌλ‘ λ‘€λ°±λΌμ. (κΈ°λ³Έμ μΌλ‘ unchecked exceptionμΌ λλ§ λ‘€λ°±)
π ν΄λμ€ λ 벨μ μ¬μ©
@Transactional @Service public class MemberService { public void method1() { // νΈλμμ μ μ©λ¨ } public void method2() { // νΈλμμ μ μ©λ¨ } }- ν΄λμ€ μ 체μ νΈλμμ μ κ±Έ μ μμ΄μ. λ¨, λ©μλμ κ°λ³μ μΌλ‘ μ€μ λ κ² μ°μ μ μ©λ©λλ€.
π rollbackFor μ΅μ
κΈ°λ³Έμ μΌλ‘
RuntimeExceptionκ³Ό κ·Έ νμ ν΄λμ€λ§ λ‘€λ°± λμμΌλ‘ κ°μ£ΌλΌμ. λ§μ½ μ²΄ν¬ μμΈ(Exception)λ λ‘€λ°±νκ³ μΆλ€λ©΄:CreatedWed, 26 Mar 2025 11:15:45 +0900