baomidou / dynamic-datasource

dynamic datasource for springboot 多数据源 动态数据源 主从分离 读写分离 分布式事务
https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
Apache License 2.0
4.76k stars 1.2k forks source link

使用@DSTransactional注解,slave库会触发表锁 #516

Closed zhujiebing closed 1 year ago

zhujiebing commented 1 year ago

Please fill it out carefully, or it will be closed. 请认真填写,不然会直接关闭。

Enviroment

JDK Version(required): 17

SpringBoot Version(required): 2.6.8

dynamic-datasource-spring-boot-starter Version(required): 3.5.1

druid Version(optional): hikariCp: spring boot默认版本

Describe what happened

麻烦看一下这个问题,我在service方法使用@DSTransactional,对两个库的表进行update,master库(mySql)不会锁表,slave库(Sqlserver)会锁表

@Mapper
public interface ModelPubMapper extends BaseMapper<ModelPub> {

}
@DS("mdm")
@Mapper
public interface ReceiptMapper extends BaseMapper<Receipt> {
}
@DSTransactional
    public void mockReceipt() {
        while (true) {
           // master库update,modelId是主键
            modelPubMapper.update(null,new LambdaUpdateWrapper<ModelPub>()
                    .set(ModelPub::getModelName,"客商模型").eq(ModelPub::getModelId,"a3d6afe546ac4cd4b0e105562d94740c"));

           // slave库update,receiptId是主键
            receiptMapper.update(null,new LambdaUpdateWrapper<Receipt>()
                    .set(Receipt::getUserId,"dev").eq(Receipt::getReceiptId,105770));
        }
    }
# 多数据源配置
spring:
  main:
    allow-circular-references: true
  datasource:
    dynamic:
      primary: foudation
      strict: false 
      hikari:
        min-idle: 2          
      datasource:    
        foudation:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://10.20.201.120:3306/dc_foundation?characterEncoding=utf8&useAffectedRows=true&useSSL=false&serverTimezone=GMT%2B8
          username: root
          password: xxxx
        mdm:
          url: jdbc:sqlserver://10.126.13.6:1433;databaseName=mdm_data;integratedSecurity=false
          username: sa
          password: xxxx
          driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://10.20.201.120:3306/dc_foundation?characterEncoding=utf8&useAffectedRows=true&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password: xxxx

Expected Result: master库和slave库进行update都只触发行锁。

Actual Result: slave库的Receipt表触发了表锁,其它会话无法对这张表进行查询。 master库的mdm_model_pub表在update的时候,其它会话可以正常对这张表进行查询访问。

7b2d9bd9-5ab8-4e8f-adac-8a2053412453

If there is an exception,or aop invalid,please attach the exception trace:

Just paste your stack trace here!

Steps to reproduce

huayanYu commented 1 year ago

触及到我知识盲区了, 不太懂.. 期待有缘人能解答你的问题.

huayanYu commented 1 year ago

Sqlserver 会锁表吗

是的,SQL Server 会对表进行锁定。在 SQL Server 中,锁是用于控制并发访问的一种机制。当一个事务对一个表执行写操作时,它会锁定该表,以防止其他事务同时进行写操作。同样,当一个事务对一个表执行读操作时,它也可以锁定该表,以防止其他事务修改该表。

SQL Server 采用多种锁定机制,包括共享锁、排他锁、行级锁等。这些锁定机制可以用来控制对表的并发访问,从而确保数据的一致性和完整性。

需要注意的是,如果一个事务长时间持有锁,那么其他事务可能会被阻塞,从而导致性能问题。因此,在设计应用程序时,需要谨慎地考虑锁定的使用,并确保尽可能地减少锁定的时间。

同样的更新语句mysql为什么不会锁表

MySQL 和 SQL Server 在锁定表的方式上有所不同。在 MySQL 中,当使用默认的存储引擎 InnoDB 时,更新语句通常会使用行级锁来锁定更新涉及到的行,而不是锁定整个表。这种行级锁定机制可以最大程度地减少锁定的范围,从而提高并发性和性能。

具体来说,当一个事务对某些行进行更新时,MySQL 的 InnoDB 存储引擎会自动为这些行添加排它锁,以防止其他事务同时更新这些行。其他事务可以继续并发地读取表中的其他行,或者对其他未被锁定的行进行更新。这种行级锁定机制可以避免长时间的锁定表,从而提高并发性和性能。

需要注意的是,MySQL 的行级锁定机制并不是完全没有锁定表的情况。当一个事务需要对整个表进行更新或者锁定时,MySQL 仍然会使用表级锁定机制。此外,行级锁定也可能会导致死锁等问题,因此在使用时需要注意锁定的范围和时间,以避免性能问题和数据不一致等风险。

SQL Server 没有行锁吗

SQL Server 是支持行级锁定的。SQL Server 提供了多种锁定机制,包括共享锁、排它锁、行级锁等。其中,行级锁是用于控制对表中特定行的并发访问的一种机制。

在 SQL Server 中,行级锁定可以由多种方式实现,例如使用SELECT ... FOR UPDATE 或 SELECT ... WITH (UPDLOCK) 语句来显式锁定某些行,或者使用隐式锁定机制来自动锁定需要更新的行。具体来说,当一个事务对某些行进行更新时,SQL Server 会自动为这些行添加排它锁,以防止其他事务同时更新这些行。其他事务可以继续并发地读取表中的其他行,或者对其他未被锁定的行进行更新。

需要注意的是,与 MySQL 不同,SQL Server 的行级锁定机制可能会导致锁定范围更大、锁定时间更长等问题,从而影响并发性和性能。因此,在使用时需要注意锁定的范围和时间,并且需要根据具体情况进行优化,以避免性能问题和数据不一致等风险。

huayanYu commented 1 year ago

根据我问GPT大概感觉是sqlserver机制问题.

poo0054 commented 1 year ago

你可以尝试不要把@DS("mdm")放在mapper上面,mapper在创建的时候会创建一个代理对象。 跟@DS("mdm")可能会造成一些不必要的影响。

poo0054 commented 1 year ago

你把代码提交一下github,我去看看

ZPZP1 commented 1 year ago

@DSTransactional只会通过控制数据库连接手动提交来控制事务启停,并不会控制锁的粒度。sqlserver没了解过,但是mysql进行update时where条件没有索引,应该也会全表扫描加next-lock类似于锁表。可以参考下看一下sqlserver的机制

zhujiebing commented 1 year ago

已解决,感谢。

ALTER DATABASE A SET READ_COMMITTED_SNAPSHOT ON;

select name, is_read_committed_snapshot_on from sys.databases;

开启Sqlserver的隔离级别 READ COMMITTED SNAPSHOT  (已经提交读隔离) 跟默认的提交读有差异。