1. Introdução aos assuntos
Uma transação é uma coleção de operações que enviam ou revogam solicitações de operação ao sistema como um todo, ou seja, essas operações são bem-sucedidas ou falham ao mesmo tempo.
2. Operações básicas de transações
2.1 Método de operação de transação 1
Exemplo: Cenário de transferência (Zhang San transfere dinheiro para Li Si)
-- 1. 查询张三账户余额
select * from account where name = '张三';
-- 2. 将张三账户余额-1000
update account set money = money - 1000 where name = '张三';
-- 此语句出错后张三钱减少但是李四钱没有增加
模拟sql语句错误
-- 3. 将李四账户余额+1000
update account set money = money + 1000 where name = '李四';
-- 查看事务提交方式
SELECT @@AUTOCOMMIT;
-- 设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效
SET @@AUTOCOMMIT = 0;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
-- 设置手动提交后上面代码改为:
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
commit;
2.2 Modo de operação de transação 2
Transação aberta: START TRANSACTION 或 BEGIN ;
Transação de confirmação: COMMIT;
Transação de reversão: ROLLBACK;
Exemplo de operação:
start transaction;
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
commit;
2.3 Casos reais de desenvolvimento
Um exemplo comum no desenvolvimento actual é o negócio de transferências no sistema bancário. Ao transferir fundos, precisamos de garantir que as alterações no montante transferido e transferido para as duas contas são atómicas, todas bem sucedidas ou todas falhadas.
Aqui está um exemplo de trecho de código:
// 假设有两个账户:accountA 和 accountB
String accountA = "A";
String accountB = "B";
double transferAmount = 100.0; // 转账金额
// 获取数据库连接
Connection connection = getConnection();
try {
connection.setAutoCommit(false); // 设置手动提交事务
// 查询账户 A 的余额
double balanceA = queryBalance(accountA);
// 查询账户 B 的余额
double balanceB = queryBalance(accountB);
if (balanceA >= transferAmount) { // 检查账户 A 的余额是否足够
// 扣除账户 A 的金额
updateBalance(connection, accountA, balanceA - transferAmount);
// 增加账户 B 的金额
updateBalance(connection, accountB, balanceB + transferAmount);
connection.commit(); // 提交事务
System.out.println("转账成功!");
} else {
System.out.println("转账失败:账户 A 余额不足!");
}
} catch (SQLException e) {
connection.rollback(); // 发生异常,回滚事务
System.out.println("转账失败:" + e.getMessage());
} finally {
connection.close(); // 关闭数据库连接
}
No exemplo acima, usamos connection.setAutoCommit(false)
a opção de desligar o commit automático de transações e controlar manualmente o commit e rollback de transações. Quando o saldo for suficiente, atualizamos o saldo da Conta A e da Conta B e usamos connection.commit()
a transação commit. connection.rollback()
Usamos transações de rollback se ocorrer uma exceção ou saldo insuficiente .
Ao usar transações, podemos garantir a consistência e confiabilidade dos dados durante as transferências. Somente quando os valores de ambas as contas forem atualizados com sucesso, a operação de envio da transação será executada, caso contrário, será revertida para o estado anterior ao início da transação.
Este é um exemplo simples: aplicativos reais podem envolver lógica de negócios e tratamento de erros mais complexos. START TRANSACTION
Mas este exemplo mostra como utilizar ou BEGIN
gerir transacções no desenvolvimento real para garantir a consistência e fiabilidade das operações de transferência.
2.4 Caso de transação Springboot
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public void transferMoney(String fromUser, String toUser, double amount) {
try {
User from = userRepository.findByUsername(fromUser);
User to = userRepository.findByUsername(toUser);
// 检查余额是否足够
if (from.getBalance() < amount) {
throw new InsufficientBalanceException("Insufficient balance in the account");
}
// 扣除转出账户的金额
from.setBalance(from.getBalance() - amount);
userRepository.save(from);
// 增加转入账户的金额
to.setBalance(to.getBalance() + amount);
userRepository.save(to);
} catch (InsufficientBalanceException e) {
// 处理余额不足的异常
throw new TransactionFailedException("Transaction failed: " + e.getMessage());
} catch (Exception e) {
// 处理其他异常
throw new TransactionFailedException("Transaction failed due to an unexpected error");
}
}
}
No exemplo acima, usamos mais opções de configuração de transação. @Transactional
Os atributos da anotação propagation
especificam o comportamento de propagação da transação. Aqui Propagation.REQUIRED
, significa que se não houver transação atual, crie uma nova transação; se já existir uma transação, adicione-a à transação atual. isolation
O atributo especifica o nível de isolamento da transação. Aqui Isolation.READ_COMMITTED
, significa ler os dados confirmados.
Durante o processo de transferência, primeiro verificamos se o saldo da conta de transferência é suficiente e, se for insuficiente, uma InsufficientBalanceException
exceção personalizada é lançada. Em seguida, atualizamos os saldos das contas de transferência e de transferência separadamente e os salvamos no banco de dados. Se ocorrerem exceções durante o processo de transferência, iremos capturá-las e tratá-las e, em seguida, lançar uma TransactionFailedException
exceção personalizada.
Este caso mostra como usar transações no Spring Boot para garantir a atomicidade das operações de transferência e lidar com algumas exceções comuns. Com base nas necessidades reais, você pode fazer mais personalizações e extensões com base na lógica de negócios.
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
É @Transactional
a configuração dos parâmetros da anotação, utilizada para especificar o comportamento de propagação e o nível de isolamento da transação.
-
propagation = Propagation.REQUIRED
: Indica que se não houver transação atual, crie uma nova transação; se já existir uma transação, adicione-a à transação atual. Este é o comportamento de propagação mais comumente usado, que garante que um conjunto de operações será bem-sucedido ou será revertido. -
isolation = Isolation.READ_COMMITTED
: Indica que o nível de isolamento da transação é "leitura confirmada". Neste nível de isolamento, uma transação só pode ler dados confirmados. Isso evita leituras sujas (leitura de dados não confirmados).
No exemplo acima, @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
o comportamento de propagação da transação é definido como padrão Propagation.REQUIRED
e o nível de isolamento é definido como Isolation.READ_COMMITTED
. Isso significa que cada transferMoney
vez que um método é chamado, uma nova transação é criada (se não houver nenhuma transação existente) e a transação só pode ler dados confirmados.
3. Quatro características dos negócios
- Atomicidade: Uma transação é uma operação mínima indivisível em que todas são bem-sucedidas ou todas falham
- Consistência: Todos os dados devem estar em um estado consistente quando a transação for concluída
- Isolamento: O mecanismo de isolamento fornecido pelo sistema de banco de dados garante que as transações sejam executadas em um ambiente independente que não seja afetado por operações externas simultâneas.
- Durabilidade: Depois que uma transação é confirmada ou revertida, suas alterações nos dados do banco de dados são permanentes
4. Problemas de transações simultâneas
pergunta | descrever |
---|---|
leitura suja | Uma transação lê dados não confirmados de outra transação |
leitura não repetível | Uma transação lê o mesmo registro sucessivamente, mas os dados lidos duas vezes são diferentes. |
leitura fantasma | Quando uma transação consulta dados de acordo com as condições, não há linha de dados correspondente, mas quando os dados são inseridos novamente, verifica-se que esta linha de dados já existe. |
Os mecanismos de controle de simultaneidade comumente usados incluem:
-
Bloqueio: Utilize bloqueios para controlar o acesso e modificação de dados por transações simultâneas, garantindo que quando uma transação lê ou modifica dados, outras transações não possam realizar a mesma operação ao mesmo tempo.
-
Nível de isolamento de transação (Nível de isolamento): Ao definir diferentes níveis de isolamento de transação, são definidas a visibilidade entre as transações e a granularidade do controle de simultaneidade.
-
Controle de simultaneidade multiversão (MVCC): quando cada transação lê dados, ela verá uma versão específica em vez de ler diretamente os dados mais recentes. Isso evita leituras sujas, leituras não repetíveis e leituras fantasmas.
-
Ordem de carimbo de data/hora: use carimbos de data/hora para classificar transações, julgar a ordem de execução das transações com base em carimbos de data/hora e garantir que as transações leiam e modifiquem os dados na ordem correta.
No desenvolvimento real, para resolver o problema das transações simultâneas, é necessário selecionar o mecanismo de controle de simultaneidade apropriado e o nível de isolamento da transação de acordo com a situação específica, e realizar um projeto e otimização razoáveis. Ao mesmo tempo, também são necessários testes e verificações suficientes para garantir que o sistema ainda possa manter a consistência e a confiabilidade dos dados em um ambiente de alta simultaneidade.
5. Nível de isolamento de transação
Nível de isolamento de transação simultânea:
nível de isolamento | leitura suja | leitura não repetível | leitura fantasma |
---|---|---|---|
Leia sem compromisso | √ | √ | √ |
Leitura confirmada | × | √ | √ |
Leitura repetível (nível de isolamento padrão) | × | × | √ |
Serializável | × | × | × |
- √ indica que o problema ocorrerá no nível de isolamento atual
- Serializable tem o desempenho mais baixo; Read uncommitted tem o desempenho mais alto e a pior segurança de dados
Nota: Quanto maior o nível de isolamento da transação, mais seguros serão os dados, mas menor será o desempenho.
Visualize o nível de isolamento da transação: Defina o nível de isolamento da transação: SESSION é o nível da sessão, o que significa que é válido apenas para a sessão atual, e GLOBAL significa que é válido para todas as sessões. SELECT @@TRANSACTION_ISOLATION;
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };