저는 Java API를 작성 중이며 MySQL을 사용하고 있습니다. 스레드 안전 읽고 데이터베이스에 쓰려면 가장 좋은 방법은 무엇입니까?
예를 들어, 다음
createUser
를
방법 :
// Create a user
// If the account does not have any users, make this
// user the primary account user
public void createUser(int accountId) {
String sql =
"INSERT INTO users " +
"(user_id, is_primary) " +
"VALUES " +
"(0, ?) ";
try (
Connection conn = JDBCUtility.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
int numberOfAccountsUsers = getNumberOfAccountsUsers(conn, accountId);
if (numberOfAccountsUsers > 0) {
ps.setString(1, null);
} else {
ps.setString(1, "y");
}
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
// Get the number of users in the specified account
public int getNumberOfAccountsUsers(Connection conn, int accountId) throws SQLException {
String sql =
"SELECT COUNT(*) AS count " +
"FROM users ";
try (
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
return rs.getInt("count");
}
}
Say source A는
createUser
를 호출합니다.
계정 ID 100에 0 명의 사용자가 있음을 읽었습니다. 그런 다음 소스 B는
createUser
를 호출합니다.
소스 A가 업데이트를 실행하기 전에 소스 B는 계정 ID가 0 명의 사용자임을 읽었습니다. 결과적으로 소스 A와 소스 B 모두 기본 사용자를 만들었습니다.
이 상황을 어떻게 안전하게 구현할 수 있습니까?
- 답변 # 1
- 답변 # 2
is_primary
를 설정하는 조건을 포함하는 것이 좋습니다 하나의 삽입 쿼리에서 값 :public void createUser(int accountId) { String sql = "INSERT INTO users (user_id, is_primary) " + "SELECT 0, if(COUNT(*)=0, 'y', null) " + "FROM users"; //.... }
따라서 멀티 스레딩 환경에서 코드를 실행하는 것이 안전하며
getNumberOfAccountsUsers
를 제거 할 수 있습니다 방법.와 동시에
와이즈 비즈 당신은 같은insert
의 가시성에 대한 의심을 없애기 위해 설명서에 명시된 바와 같이 (@tsolakp 주석 덕분에) :The results of a concurrent INSERT may not be visible immediately.
또는
Connection
에 고유 인덱스 사용 열, (is_primary
라고 가정합니다. 그리고y
가능한 가치입니다. 여러 개의null
참고 값은 모든 MySql 엔진에서 허용되므로) 고유 제약 조건 위반의 경우null
를 다시 실행하면됩니다. 질문. 이 고유 한 인덱스 솔루션은 기존 코드에서도 작동합니다.insert
- java : 웹 앱을 처음 시작할 때 데이터베이스를 만들고 채우려면 어떻게 해야 합니까?
- submit() executorService Java [중복]
- java : Redis 서버가 JedisConnectionException 대신 NULL을 반환하도록 할 수 있습니까?
- Java Netbeans를 사용하여 getGeoLocation twitter4j를 사용하여 MySQL로 데이터를 가져오는 방법
- java : 메인 스레드와 종료
- python : 동기 스레드와 비동기 스레드를 구별하는 방법은 무엇입니까?
- java : 큐를 사용하는 생산자/소비자 스레드
- java : 이클립스의 SQL 쿼리
- mysql : Azure VM에서 Wildfly 서버를 확장하는 방법
- java : ForkJoinPool::shutdown 대 ForkJoinPool::shutdown지금 ForkJoinPool::join 이후
질문 개요
먼저, 질문은 스레드 안전성과 관련이 없으며 더 잘 최적화 될 수있는 트랜잭션 및 코드와 관련이 있습니다.
이 내용을 올바르게 읽으려면 첫 번째 사용자를 기본 사용자로 설정하십시오. 아마이 접근법을 전혀 사용하지 않을 것입니다 (즉, 첫 번째 사용자에 대해 is_primary 행을 가짐). 그렇다면 Java 응용 프로그램에 해당 조건을 전혀 포함시키지 않을 것입니다. 사용자를 생성 할 때마다 조건부로 평가 될뿐만 아니라 데이터베이스를 불필요하게 호출하게됩니다. 대신 나는 이렇게 리팩토링 할 것이다.
코드먼저 식탁이 이런 식인지 확인하십시오.
즉, user_id는auto_increment
여야합니다. 그리고not null
. 나는name
를 포함 아래 요점을 설명하는 데 도움이되기 때문에 열 (user_id
이외의 사용자 테이블의 다른 열 대신 사용할 수 있음) 그리고is_primary
). 나는 또한user_id
를했다 아마도 기본이기 때문입니다.현재 테이블 만 수정하려면 다음과 같이하십시오.
그런 다음 행을 삽입 할 때마다 첫 번째 행인지 확인하고 그에 따라 업데이트되도록 트리거를 만듭니다.
그 시점에서 당신은 단지
createUser
를 가질 수 있습니다 새 레코드를 데이터베이스에 삽입하는 메소드로user_id 또는 기본 필드를 전혀 지정할 필요가 없습니다. 테이블이 다음과 같다고 가정 해 봅시다.createUser 메소드는 본질적으로 다음과 같습니다.
사용자 테이블의 두 배는 다음과 같습니다.
그러나 ... 그러나 위에서 언급 한 해결책이 원래의 질문에서 제안 된 것보다 낫다고 생각하지만 여전히 이것을 처리하는 최적의 방법이라고 생각하지 않습니다. 제안하기 전에 프로젝트에 대해 더 알아야합니다. 첫 번째 사용자 기본은 무엇입니까? 기본 사용자 란 무엇입니까? 기본 사용자가 하나만있는 경우 기본 사용자를 수동으로 설정할 수없는 이유는 무엇입니까? 관리 콘솔이 있습니까? 기타 질문이 예시라면 ...따라서 질문이 단순히 트랜잭션 관리에 대한 더 큰 질문을 설명하는 데 사용 된 예인 경우 거짓 연결에 자동 커밋을 사용하여 수동으로 트랜잭션을 관리 할 수 있습니다. 자동 커밋에 대한 자세한 내용은 Oracle JDBC Java 설명서를 참조하십시오. 또한 위에서 언급 한 것처럼 특정 작업에 따라 테이블/행 수준 잠금을 변경할 수 있지만이 문제에 대한 비현실적인 해결책이라고 생각합니다. 선택을 하위 쿼리로 포함시킬 수도 있지만 다시는 나쁜 연습에 반창고를 사용하는 것입니다.
기본 사용자 패러다임을 완전히 바꾸고 싶지 않다면 이것이 가장 효율적이고 체계적인 방법이라고 생각합니다.