事务超时

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 本文概览:介绍了超时有关的概念:@Transaction的timeout、mybatis的timeout、mysql的innodb_lock_wait_timeout。

1 问题

1.1 背景

在一个事务中完成解析一个大文件,分批存入到数据库。遇到问题,执行时间比较长,就讨论了事务超时的问题,担心执行时间太长,事务超时自动回滚了。

为了考虑这个问题,需要考虑如下超时相关的设置:

  • 一个事务的超时时间。spring的@Transactional
  • 一个stametn的执行时间。包括mybais的timeout、jdbc的timeout和mysql中的innodb_lock_wait_timeout
  • 一个connection的持续时间。myql中的wait_timeout

1.2 问题的结果

我们目的时时间执行时间过长不会被中止。所以:

  • 就不需要设置@Transactional了,因为@Transaction设置的目的是为了在一个事务执行时间超过某一个预知时抛出异常。
  • 对于mybatis的timeout也不需要设置,因为这个设置也是为了在某一个statemnet时间太久时被中止。
  • 对于myql的innodb_lock_wait_timeout,这个是针对两个事务同时更新同一行记录时,等待的最长时间,因为我们都是新增数据,所以也不需要考虑。
  • 对于connection的持续时间wait_timeout。这个时间是这个connection执行最近一个statement到当前的时间间隔,因为我们这个没解析100条记录就执行一次insert,所以两次insert的时间间隔很短,所以也不会超时。

综上,我们不需要额外的进行设置。

2 Spring的 @Transactional的超时

2.1 作用

定义一个事务的超时时间,一个事务中包含很多statement。

2.2 源码实现

1. spring实现超时

(1)根据timeout+当前时间点 赋值给一个deadLine。

(2)每一次执行sql,就会获取到一个statement时,计算liveTime =(deadline- 当前时间),分如下两种情况处理:

  • 如果liveTime>0,此时就执行stament.setQueryTimeout(liveTime);
  • 如果liveTime < 0,此时就抛出异常

(3)总结

spring实现超时通过deadLine和jdbc的stament#setQueryTime两种策略来判断超时。

2、源码分析

如果选择DataSourceTransactionManager,事务内所有的sql操作必须通过JdbcTemplate执行才能使timeout设置正常工作,通过myBatis执行的sql操作将无法应用超时设置。

(1)在开启事务时将注解中timeout赋值给connectionHolder

DataSourceTransactionManager#doBegin
           txObject.getConnectionHolder().setTimeoutInSeconds(timeout);

(2)在ResourceHolderSuport中提供了超时校验

public long getTimeToLiveInMillis() throws TransactionTimedOutException{
   if (this.deadline == null) { 
       throw new IllegalStateException("No timeout specified for this resource holder");
   }
   long timeToLive = this.deadline.getTime() - System.currentTimeMillis();
   // 校验timeout
   checkTransactionTimeout(timeToLive <= 0);
   return timeToLive;
}
// 如果超时抛出异常
private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
   if (deadlineReached) {
      setRollbackOnly();
      throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
   }

(3)查看那些地方用到了上述接口

在DataSourceUtils中的applyTransactionTimeout和applyTiemou用到了上述接口,如下

public static void applyTransactionTimeout(Statement stmt, DataSource dataSource) throws SQLException {
    applyTimeout(stmt, dataSource, 0);
  }
public static void applyTimeout(Statement stmt, DataSource dataSource, int timeout) throws SQLException {
    Assert.notNull(stmt, "No Statement specified");
    Assert.notNull(dataSource, "No DataSource specified");
    ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
    if (holder != null && holder.hasTimeout()) {
      // 设置statment的超时时间
      stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());
    }
    else if (timeout > 0) {
      // No current transaction timeout -> apply specified value.
      stmt.setQueryTimeout(timeout);
    }
  }

(4)再查看用到DataSourceUtils的applyTimeout和applyTransactionTimeout的地方

  • 用到applyTimeout的地方。JdbcTemplate#applyStatementSettings。
  • 用到applyTransactionTimeout的地方,TransactionAwareDataSourceProxy#TransactionAwareInvocaitonHandler.invoke

(5)暂时没有发现使用TransactionAwareDataSourceProxy的地方,所以只需要再查看使用JdbcTemplate#applyStatementSettings地方:在JdbcTemplate的如下3个函数中使用。所以可以理解使用@Transactional的timeout的时候,必须要使用jdbcTemplate实现dao,而不能通过mybatis。

public <T> T execute(CallableStatementCreator csc, CallableStatementCallback<T> action){
}
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action){
}
public <T> T execute(StatementCallback<T> action) throws DataAccessException {
}

2.3 复现@Transactional的超时

由上分析,使用@Transactional的timeout属性时,需要使用JDBCtemplate实现dao,而不能使用Mybatis。JdbcTemplate配置可以参考: JDBCtemplate实例 。

1. 使用JDBC定义一个dao

@Service
public class StudentDaoWithJdbcTemplate {
    @Resource
    private JdbcTemplate jdbcTemplate;
    public void update() {
        jdbcTemplate.execute(new StatementCallback<Integer>() {
            public Integer doInStatement(Statement stmt) throws SQLException, DataAccessException {
                stmt.execute("update student set name='success511' where id = 1;");
                return 1;
            }
        });
    }
}

2、设置事务超时的service

@Service
public class TimeOutService {
    private static final Logger logger = LoggerFactory.getLogger(TimeOutService.class);
    @Resource
    private StudentDaoWithJdbcTemplate studentDaoWithJdbcTemplate;
      /**
     * 测试注解@Transactional的超时timeout---jdbcTemlate实现dao
     */
    @Transactional(value = "transactionManager", timeout = 1)
    public void testAnnotationTransactionalTimeOutWithJdbcTemplate() {
        try {
            Thread.sleep(3000);
            logger.info("sleep 3s end");
        } catch (Exception e) {
        }
        studentDaoWithJdbcTemplate.update();
    }
}

3、写一个测试函数

/**
     * 测试@Transactional的超时属性---Template
     */
    @Test
    public void testAnnotationTransactionalServiceTestWithJdbcTemplate(){
        timeOutService.testAnnotationTransactionalTimeOutWithJdbcTemplate();
    }

执行结果为

org.springframework.transaction.TransactionTimedOutException: Transaction timed out: deadline was Thu Dec 08 10:22:39 CST 2016

3 Mybatis层的timeout

3.1 作用

就是设置一个stament的执行时间,包括update、delte、select和select。

3.2 源码实现

通过jdbc的statement的setQueryTimeout来实现。如下在BaseStatemntHandler中setStatementTimeout来设置超时。

// 设置超时属性
 protected void setStatementTimeout(Statement stmt) throws SQLException {
    Integer timeout = mappedStatement.getTimeout();
    Integer defaultTimeout = configuration.getDefaultStatementTimeout();
    if (timeout != null) {
      stmt.setQueryTimeout(timeout);
    } else if (defaultTimeout != null) {
      stmt.setQueryTimeout(defaultTimeout);
    }
  }

3.3  复现Mybatis超时

(1)mapper.xml

设置超时时间为“1”。如下

<update id="updateCheckTimeOut" parameterType="int" timeout="1">
       UPDATE
        student
        SET
        name = 'timeout'
        WHERE
        id = #{id}
  </update>

(2)dao接口

@Repository
public interface StudentDao {
    /**
     * 测试mybatis中timeout字段
     * @param id
     */
    void updateCheckTimeOut(int id);
}

(3)service

@Service
public class TimeOutService {
    private static final Logger logger = LoggerFactory.getLogger(TimeOutService.class);
    @Resource
    private StudentDao studentDao;
    /**
     * 测试mybatis配置的超时,这里还需要开启事务,通过利用不同事务中同时更新同一条语句时,需要等待行锁,来实现增长sql的执行时间
     */
    @Transactional(value = "transactionManager")
    public void testMybatisTimeOut() {
        studentDao.updateCheckTimeOut(1);
        try {
            Thread.sleep(3000);
            logger.info("sleep 3s end");
        } catch (Exception e) {
        }
    }
}

(4)测试类

/**
     * 测试mybais的timeout
     */
    @Test
    public void testMybatisTimeOut(){
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                timeOutService.testMybatisTimeOut();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                timeOutService.testMybatisTimeOut();
            }
        });
        t1.start();
        t2.start();
        // 为了查看子线程执行情况,所以这里使用while-true
        while(true){
        }
    }

结果为:

### SQL: UPDATE         student         SET         name = ‘timeout1’         WHERE         id = ?
### Cause: java.sql.SQLException: Query execution was interrupted

4 JDBC的timeout

介绍了JDBC的query timeout 和 socket timeout。

4.1 QueryTiemout

4.1.1  作用

就是设置stament执行时间。

4.1.2 配置

1、 默认情况下为0,即不生效。

2、 设置

通过Statement的setQueryTimeout来设置。如下

void setQueryTimeout(int seconds) throws SQLException;

4.1.3 Statement的QueryTimeout处理过程

在执行stament时,会同时建立一个检查超时的线程,如果statment执行超时了,这个线程会向mysql的server端发送一个取消执行的请求,而不是等执行完了statment才统计时间,判断是否超时。具体流程如下:

  • 通过调用Connection的createStatement()方法创建statement
  • 调用Statement的executeQuery()方法
  • statement通过自身connection将query发送给MySQL数据库
  • statement创建一个新的timeout-execution线程用于超时处理
  • 5.1版本后改为每个connection分配一个timeout-execution线程
  • 向timeout-execution线程进行注册
  • 达到超时时间
  • TimerThread调用JtdsStatement实例中的TsdCore.cancel()方法
  • timeout-execution线程创建一个和statement配置相同的connection
  • 使用新创建的connection向超时query发送cancel query(KILL QUERY “connectionId”)

ab9eb8f217553d509aedb1ec258cecf.png

4.2 JDBC Socket Timeout

4.2.1 作用

1、作用

在数据库被突然停掉或是发生网络错误(由于设备故障等原因)时,如果没有设置socket timeout的话,应用在数据库返回结果前会无期限地等下去,这种连接被称为dead connection。为了能够避免应用在发生网络错误时产生无休止等待的情况,所以需要设置socket timeout。

注意:socket timeout的值必须要高于statement timeout,否则,在网络正常的情况下,socket timeout将会先生效而statement timeout就失效。

2、socket timeout包含两种timeout:

(1)socket连接时的timeout:connectionTimeout

设置之后会抛出异常 java.net.SocketTimeoutException: connect timed out

(2)socket读写时的timeout:socketTimeout

设置之后会抛出异常 :java.net.SocketTimeoutException: Read timed out

4.2.2 配置

1、默认情况

connectTimeout和socketTimeout都是0,为0时表示不生效。

2、必须需要同时设置connectTimeout和sockTimeout,如下两种方式

(1)通过URL方式设置

jdbc:mysql://xxx.xx.xxx.xxx:3306/database?connectTimeout=60000&socketTimeout=60000复制代码

(2)spring在xml时设置dataSource时,如下在配置duridDataSource的connectionTimeout和socketTimeout属性,可以理解为druidDataSource通过这两个属性来拼接成上述URL中的connectionTimeout和socketTimeout。

<bean id="transDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="close">
        ..........
         <property name="connectProperties">
             <props>
                <prop key="connectTimeout">1000</prop>
                <prop key="socketTimeout">1000</prop>
             </props>
         </property>
        ..............
    </bean>

4.2.3 JDBC的conetionTimeout和socketTimeout的底层实现

1、socket连接时的timeout:connectionTimeout。

通过Socket.connect(SocketAddress endpoint, int timeout)设置

socket = new Socket();
    long t1 = 0;
    try {
        t1 = System.currentTimeMillis();
        // 设置connect timeout 为2000毫秒
        socket.connect(new InetSocketAddress("www.ss.ssss", 8080), 2000);
    } catch (IOException e) {
        long t2 = System.currentTimeMillis();
        e.printStackTrace();
        System.out.println("Connect failed, take time -> " + (t2 - t1) + "ms.");
    }

2、socket读写时的timeout:socketTimeout

通过Socket.setSoTimeout(int timeout)设置。

socket = new Socket();
    socket.connect(new InetSocketAddress("localhost", 8080));
    // 设置so timeout 为2000毫秒
    socket.setSoTimeout(2000);
    System.out.println("Connected.");
    in = socket.getInputStream();
    System.out.println("reading...");
    t1 = System.currentTimeMillis();
    in.read();

注意:如果不设置此属性,那么如果server端突然中断,那么客户端的就会一直阻塞在in.read上面。

4.2.4 遇到过的一个问题

1、 问题描述

之前在做一个项目时,程序运行在进行查询数据库时经常hang住了。

2、分析

在hang住的时候通过jstack打印的堆栈信息如下:

"main" prio=10 tid=0x00007ff27400a000 nid=0x7d88 runnable [0x00007ff2792d5000]
   java.lang.Thread.State: RUNNABLE
  at java.net.SocketInputStream.socketRead0(Native Method)
  at java.net.SocketInputStream.read(SocketInputStream.java:152)
  at java.net.SocketInputStream.read(SocketInputStream.java:122)
  at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:113)
  at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:160)
  at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:188)
  - locked <0x00000006bca6d6c0> (a com.mysql.jdbc.util.ReadAheadInputStream)
  at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2331)
  at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2776)
  at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2765)
  at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3301)
  at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1853)
  at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1976)
  at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2497)
  - locked <0x00000006bca69b00> (a java.lang.Object)
  at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2426)
  at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1383)
  - locked <0x00000006bca69b00> (a java.lang.Object)
  at com.mysql.jdbc.ConnectionImpl.getTransactionIsolation(ConnectionImpl.java:3074)
  at com.alibaba.druid.filter.FilterChainImpl.connection_getTransactionIsolation(FilterChainImpl.java:347)
  at com.alibaba.druid.filter.FilterAdapter.connection_getTransactionIsolation(FilterAdapter.java:871)
  at com.alibaba.druid.filter.FilterChainImpl.connection_getTransactionIsolation(FilterChainImpl.java:344)
  at com.alibaba.druid.filter.FilterAdapter.connection_getTransactionIsolation(FilterAdapter.java:871)
  at com.alibaba.druid.filter.FilterChainImpl.connection_getTransactionIsolation(FilterChainImpl.java:344)
  at com.alibaba.druid.filter.FilterAdapter.connection_getTransactionIsolation(FilterAdapter.java:871)
  at com.alibaba.druid.filter.FilterChainImpl.connection_getTransactionIsolation(FilterChainImpl.java:344)
  at com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.getTransactionIsolation(ConnectionProxyImpl.java:260)复制代码

由上面的堆栈信息,发现是卡在了socketInputStream.read上面,此时只要配置socketTimeout就可以终止hang住的程序了。即通过配置druidDataSource的connectionTimeout和socketTimeout两个属性来实现,此时就不会hang住了,而是会报如下异常:

Caused by: java.net.SocketTimeoutException: Read timed out
  at java.net.SocketInputStream.socketRead0(Native Method)
  at java.net.SocketInputStream.read(SocketInputStream.java:152)
  at java.net.SocketInputStream.read(SocketInputStream.java:122)
  at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:100)
  at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:143)
  at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:173)
  at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2911)
  at com.mysql.jdbc.MysqlIO.nextRowFast(MysqlIO.java:2116)复制代码

3、总结

访问数据库时必须配置这两个属性。可以在配置连接池dataSource时,配置connectionTimeout和socketTimeout两个属性来实现。或者直接通过URL指定这两个属性

jdbc:mysql://xxx.xx.xxx.xxx:3306/database?connectTimeout=60000&socketTimeout=60000复制代码

5 Mysql层的indb_lock_wait_timeout

5.1 理解

1. 作用

它只针对 行锁(row lock),不同事务同时更新同一行的时候,等待其他事务提交的时间,所以一个事务中如果新增数据,那么不会受到事务超时影响。

2.变量说明

(1)变量说明

  • innodb_lock_wait_timeout(超时时间),事务等待行锁的时间。默认是50s.

(2)查看上面的两个变量

show variables like '%timeout%'复制代码

如下结果

b7dc613c80b0a680d52de3a8e09f684.png

(3)设置超时变量

set GLOBAL innodb_lock_wait_timeout = 10;复制代码

3. 一个相关的变量

innodb_rollback_on_timeout ,事务等待超时之后,进行回滚的策略。默认是off,表示只回滚到上一个statement,每执行一个sql语句(一个satement)就有一个保存点,此时是回滚到上一个保存点。如果这个值改为on,那此时事务就进行整体回滚,即执行了rollback。

5.2 复现

5.2.1 在终端复现

1. 实现复现事务超时

(1)不发生事务超时的情况

开启两个终端,分别执行事务,此时事务2可以执行commit,也可以一直等待下去不会出现事务超时,因为此时事务2都是新增数据,不会涉及到行锁(行锁只发生在更新数据或者删除数据)。

6c4cee859eb1089f74c5eebd438de94.png

(2)发生事务超时

f953676fe18532f6278b31123707022.png

  • 首先我们开一个终端执行上面事务1。
  • 然后再开一个终端执行事务2,此时执行事务2的时候就会hang住(如上图),一直等待下去,直到事务超时,报错“ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction”。此时可以继续执行sql,或者执行commit,或者rollbac。如下图是一个完整的例子。

b5adf9f209aef8e13c4f34d28171944.png

(3)比较 innodb_rollback_on_timeoue为false和ture的两种情况

上面的情况下是在 innodb_rollback_on_timeout=false的情况下进行的,那么 innodb_rollback_on_timeout=true时,则表示回滚了整个事务。此时再执行一个sql,就跟原来的事务没有关系了。

5.2.2 在代码中事务超时的复现

1、mysql设置

# 设置超时变量,单位是s
set GLOBAL innodb_lock_wait_timeout = 10;

2、代码

(1)Dao接口

@Repository
public interface StudentDao {
    void updateById(int id);
}

(2)事务函数

/**
     * 测试事务超时
     */
    @Transactional(value="transactionManager")
    public void excutetransactionTimeOut(){
        logger.info("f1 beg");
        studentDao.updateById(1);
        while(true){}
    }

(3)测试

/**
     * 测试事务超时
     */
    @Test
    public void testTransactionTimeOut() {
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                transactionService.executeTransactionTimeOut();
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                transactionService.executeTransactionTimeOut();
            }
        });
        t1.start();
        t2.start();
        while (true){
        }
    }

(4)查看连接线程show processlit

fdac969e4e107ef6b9f986f0574f8ae.png

(5)等待10s之后,抛出异常如

org.springframework.dao.CannotAcquireLockException:
### Error updating database.  Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
### The error may involve dao.datasource1.StudentDao.updateById-Inline
### The error occurred while setting parameters
### SQL: UPDATE         student         SET         name = ‘success24’         WHERE         id = ?
### Cause: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction

5.3  mysql 其他时间变量的解释

1. wait_timeout

参考:blog.csdn.net/cenfei78325…

就是一connection的sleep多长时间,通过show processlist来查看一个连接的sleep时间,这个时间是这个connection执行最近一个statement到当前的时间间隔。这个时间就是设置开一个客户端,不做任何操作的时候的连接保持时间。

2. 慢查询查询时间

b3e389a5b17f5b48264ccff0fd8fd9c.png

6 各种TimeOut之间关系

上面结果timeout的关系,可以通过中一个www.importnew.com/2466.html图来表示。

(1)Spring、Mybatis和JDBC级别的超时并没有更改mysql的属性。可以查看三者的实现:

  • 对于Spring的@transactional是通过timeout属性初始化了一个deadline,每一次创建statment判断deadline是否小于0,如果小于0抛异常;否则通过JDBC的statement#setQueryTimeout来设置超时
  • Mybatis的timeout也是通过通过JDBC的statement#setQueryTimeout来设置超时。
  • JDBC的timeout,是在stament执行时,开启了一个监听线程,发现超时,就终端当前执行的stament,然后抛异常。

(2)只有mysql层没有超时的情况下。上层的JDBC或者spring层的timeout才有意义。

f6248ca68013b4c0106c2385b693b5d.png

7 参考文献

【1】JDBC的timeout http://www.importnew.com/2466.html

【2】@Tranasaction 的timeout生效 http://www.cnblogs.com/iceJava/p/4500794.html

【3】java SocketTimout  https://my.oschina.net/shipley/blog/715196

(全文完)




相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
4月前
|
数据库连接 数据库 UED
超时时间设置过短会导致哪些问题
超时时间设置过短可能导致正常请求被误判为超时,引起服务中断、数据丢失或不完整,影响用户体验和系统稳定性。
153 13
|
6月前
|
NoSQL 前端开发 Java
【幂等性大坑】事务提交前释放锁导致锁失效问题
在事务中,使用了 Redis 分布式锁.这个方法一旦执行,事务生效,接着就 Redis 分布式锁生效,代码执行完后,先释放 Redis 分布式锁,然后再提交事务数据,最后事务结束。如果是表单重复提交场景,可以尝试给“订单号”等有唯一性的字段加唯一索引,这样重复提交时会因为唯一索引约束导致索引失效。5、如果表的一个字段,要作为另外一个表的外键,这个字段必须有唯一约束(或是主键),如果只是有唯一索引,就会报错。2、创建唯一约束,会自动创建一个同名的唯一索引,该索引不能单独删除,删除约束会自动删除索引。
|
8月前
|
SQL Oracle 关系型数据库
第7章 事务
第7章 事务
44 0
|
数据库
【并发事务会产生哪些问题】
【并发事务会产生哪些问题】
165 0
|
10月前
|
数据库连接 数据库
多线程事务失效的原因
【5月更文挑战第16天】多线程事务失效的原因
494 0
|
10月前
事务提交后回调
事务提交后回调
56 2
|
关系型数据库 MySQL 数据库
并发事务更新问题
并发事务更新问题
77 0
|
数据库
并发事务带来哪些问题?
并发事务带来哪些问题?
170 0
|
SQL Java 大数据
事务详解(2)
你好看官,里面请!今天笔者讲的是事务。不懂或者觉得我写的有问题可以在评论区留言,我看到会及时回复。 注意:本文仅用于学习参考,不可用于商业用途,如需转载请跟我联系。
179 1

热门文章

最新文章