我对autocommit以及select语句是否需要加事务的一点理解

本文探讨了MySQL中的autocommit特性和事务使用,解释了autocommit如何影响DML语句,并讨论了在不同隔离级别下读写操作的一致性和原子性。在执行SQL语句时,数据库是否会自动开启事务存在争议,但实践表明,即使不显式开启事务,DML操作的结果也会被提交或回滚。在使用Hibernate时,对于查询操作无需手动管理事务,而对于DML操作则建议显式控制。

最近在复习hibernate以及数据库事务和隔离级别的相关知识,有了一点新的理解。mysql可以通过下面的命令:查询和修改当前客户端连接的autocommit。0代表不自动提交,1代表自动提交。


什么是自动提交呢?对于insert、update、delete这些DML语句来说,如果没有提交,被修改的记录处于uncommited状态的;如果提交了,那么处于commited状态的。下面的客户端1和2设置的事务隔离级别都是repeatable read,所以客户端2看不到客户端1的修改。如果客户端1打开了autocommit,那么客户端2就可以查看到客户端1修改后的结果。

     


mysql如果我们想开启一个事务,可以通过start  transaction;如果提交这个事务用commit;如果需要放弃回滚,那么可以使用rollback。

#提交一个事务
start  transaction;
	#some sql statements
commit;

#回滚事务
start  transaction;
	#some sql statements
rollback;

可以看到autocommit和事务很像,我自己认为:执行第一条SQL语句的时候,如果我们没有显示地开启事务,那么数据库服务器会自动帮我们开启一个事务;如果打开了autocommit,每一条sql执行结束的时候,数据库服务器会自动关闭事务。如果关闭了autocommit,那么数据库就不会自动帮我们提交事务。


也就是说:如果开启了autocommit,数据库服务器自动开启事务(每一条sql语句开始执行的时候),自动提交事务(sql语句执行成功),自动回滚事务(sql语句执行失败)。很显然:autocommit没有什么实际意义,如果要使用事务,就必需关闭autocommit,不然每一条sql都是一个独立的事务,而实际上事务包含了一组sql语句。最佳实践:对于DML操作,我们需要显示开始、显示提交或者回滚事务,哪儿怕事务里面只有一条sql语句

public void testHibernateSave() {
	Student student = new Student();
	student.setName("saveWithHibernate");
	student.setAge(3);

	Session session = sessionFactory.openSession();
	session.beginTransaction();//开启事务
	session.save(student);

	session.save(student);

	session.getTransaction().commit();//提交事务
	session.close();
}


现在我们理解autocommit和事务了,那我们再看一下select语句,select语句需要加事务吗?
public void testSelect() {
	Session session = sessionFactory.openSession();
	Student student = (Student) session.get(Student.class, 100);
	System.out.println(student);
	session.close();
}


public void testSelectWithTransaction() {
	Session session = sessionFactory.openSession();
	session.beginTransaction();//开启事务

	Student student = (Student) session.load(Student.class, 100);

	System.out.println(student);
	
	session.getTransaction().commit();//提交事务
	session.close();
}
上面的2种查询方式,都能从数据库查出我们需要的结果。那到底select的时候需不要加事务呢?这个问题,网上有很多争论,有人说需要加,有人说不需要,比如iteye上的这个帖子。我来谈下自己的看法,不一定对,有错误的话欢迎指正。

读操作最重要的是什么呢?一致性。写操作最重要的是什么吗呢?原子性。如果一个客户端(占用一个数据库连接),只需执行要一条select,那么显然加不加事务没有任何影响。如果一个客户端,需要执行多条select,对于这个客户端来说,最重要的就是读的一致性了。比如我第一次读取这条记录,第二次再次读取这条记录,客户端肯定希望这2次的读取结果是一模一样的。



客户端1的事务隔离级别是:REPEATABLE-READ,关闭了autocommit,如果客户端1没有显示地commit,那么永远也看不见客户端2修改后的数据。如果客户端1显示提commit之后,再次查询,就能够读取到客户端2修改后的结果了。我们知道:一个数据库连接可以开启多个数据库事务。至于一个数据库事务里面多次查询结果是否一致,是取决于事务隔离级别的。不同的隔离级别能够解决不同的数据库并发问题:



所以我认为:如果我们没有显示地开启事务,那么当执行第一条sql语句(不管是select,还是DML)都会开启一个事务。如果打开了autocommit,那么sql执行成功后会自动提交;如果关闭了autocommit,那么我们必须要显示地commit或者rollback。

public void testSelect() {
	Session session = sessionFactory.openSession();
	Student student = (Student) session.get(Student.class, 100);
	System.out.println(student);
	session.close();
}

hibernate默认会关闭autocommit,上面的代码没有显示开启和提交事务。当执行select的时候,底层的数据库连接会自动开启事务,我们没有显示地提交事务,这是因为session.close会释放底层的数据库连接,数据库连接被释放,没有提交的事务会被自动回滚。所以使用hibernate的时候,DML操作我们需要显示地开启事务和提交事务,如果是查询操作不需要显示地开启和提交事务


写到这里,有一个地方是存在争议的:如果没有显示地开启事务,mysql数据库到底会不会在执行第一条SQL的时候自动开始事务。从上面的测试结果来看,应该是会的,或者说至少也会开启一个类似事务的东东(出于性能的考虑)。因为我们没有显示地开启事务,执行DML语句后commit,这些修改确实被永久保存到了数据库。而且执行select后,如果不commit,那么后续的select永远也看不到另一个客户端的修改结果。从表现上来看,这跟事务没有什么两样。


<think>我们正在解决用户关于PL/SQLDeveloper执行SELECT查询后无法中断的问题。根据用户提供的引用和问题描述,用户遇到的情况是:在PL/SQLDeveloper中执行SELECT查询后,无法通过界面操作中断查询。可能的原因和解决方法如下:原因分析:1.查询执行时间过长:当查询涉及大量数据或复杂计算时,数据库需要较长时间处理,此时界面可能暂时无响应。2.锁等待:查询可能因为等待资源(如锁)而挂起,导致无法中断。3.PL/SQLDeveloper工具本身的问题:工具可能在某些情况下无法正常发送中断请求。4.网络问题:网络中断可能导致客户端与服务器之间的通信失败,但查询仍在服务器端运行。解决方法:1.在数据库层面终止会话:-首先需要找到正在运行的会话的SID和SERIAL#,可以通过以下SQL查询:```sqlSELECTs.sid,s.serial#,s.status,s.machine,s.program,s.sql_id,s.actionFROMv$sessionsWHEREs.username='你的用户名';--替换为实际用户名```-然后使用查到的SID和SERIAL#终止会话:```sqlALTERSYSTEMKILLSESSION'sid,serial#';```2.使用PL/SQLDeveloper提供的停止按钮:在查询执行过程中,工具栏上会有一个“停止”按钮(红色方块),点击尝试中断查询。但用户已经遇到无法中断的情况,所以可能需要使用数据库层面的终止。3.检查网络连接:确保网络连接稳定,因为网络问题可能导致客户端失去与服务器的连接,但服务器上的查询可能仍在运行。4.优化查询:如果查询涉及大量数据,考虑优化查询语句(如添索引、减少返回行数等),以减少执行时间。5.设置超时参数:在PL/SQLDeveloper中,可以设置查询超时时间(Tools->Preferences->Connection->OCI/ORACLE->SQL*Plus选项卡中的“Timeoutafter...seconds”),这样查询在指定时间后会自动停止。6.使用其他工具:如果PL/SQLDeveloper频繁出现此问题,可以尝试使用其他数据库客户端工具(如SQL*Plus、SQLDeveloper等)执行查询,看是否同样出现无法中断的问题。7.检查数据库服务器状态:如果数据库服务器负载过高,可能导致响应缓慢,此时需要检查数据库服务器性能。注意:在终止会话时,如果会话正在进行事务操作,终止会话可能会导致事务回滚,需要谨慎操作。根据引用[1],用户提到使用sqlplus长时间跑脚本不会出现通信中断,而PL/SQLDeveloper有自动断开机制。这可能是PL/SQLDeveloper的会话超时设置导致的。因此,检查并调整PL/SQLDeveloper的超时设置也是一个方向。另外,引用[4]提供了一种查询锁定记录的方法,可以用来检查是否因为锁等待导致无法中断:```sqlselectobject_name,machine,s.sid,s.serial#fromv$locked_objectl,dba_objectso,v$sessionswherel.object_id=o.object_idandl.session_id=s.sid;```如果发现锁等待,可以按照上述方法终止阻塞的会话。总结步骤:1.尝试在PL/SQLDeveloper中使用停止按钮。2.如果无效,则通过查询会话信息,在数据库层面终止会话。3.优化查询语句和检查数据库性能,避免长时间查询。4.调整PL/SQLDeveloper的超时设置。5.如问题持续,考虑更换客户端工具或检查网络。希望这些方法能帮助用户解决问题。</think>### PL/SQL Developer 执行 SELECT 查询无法中断的原因及解决方法 #### 常见原因分析 1. **长时间运行查询** 复杂查询或大数据量操作导致执行时间过长,客户端界面可能暂时无响应(类似引用[1]描述的通信中断现象)。 2. **会话阻塞** 查询可能被其他事务锁阻塞(如DML操作未提交),导致挂起: ```sql -- 检查锁阻塞(引用[4]方法) SELECT object_name, machine, s.sid, s.serial# FROM v$locked_object l, dba_objects o, v$session s WHERE l.object_id = o.object_id AND l.session_id = s.sid; ``` 3. **工具自身限制** PL/SQL Developer 的会话管理机制可能导致中断信号无法传递到数据库(引用[1]中提到的自动断开机制问题)。 4. **网络/资源问题** 网络波动或数据库服务器资源(CPU/内存)耗尽导致响应延迟。 #### 解决方案 ##### 方法1:强制终止会话(推荐) 1. **定位会话信息**: ```sql SELECT sid, serial#, username, status, sql_id FROM v$session WHERE type = 'USER' AND sql_id IS NOT NULL; ``` 2. **终止会话**: ```sql ALTER SYSTEM KILL SESSION '<sid>,<serial#>'; -- 示例:ALTER SYSTEM KILL SESSION '123,4567'; ``` ##### 方法2:优化查询行为 - **添超时限制**(Oracle 12c+): ```sql SELECT /*+ MAX_EXECUTION_TIME(3000) */ * FROM large_table; -- 单位:毫秒 ``` - **分页查询**: ```sql SELECT * FROM (SELECT t.*, ROWNUM rn FROM big_table t) WHERE rn BETWEEN 1 AND 100; ``` ##### 方法3:调整PL/SQL Developer设置 1. **启用查询超时**: `Tools > Preferences > Connection > OCI/ORACLE > SQL*Plus` → 设置 `Timeout after [ ] seconds` 2. **关闭自动提交**: `Tools > Preferences > Connection > AutoCommit` → 设为 `Off` ##### 方法4:替代工具验证 使用 SQL*Plus 执行相同查询(引用[1]验证方法): ```shell sqlplus user/pass@db > SET TIMING ON > SELECT ... FROM ...; -- 按Ctrl+C可强制中断 ``` #### 预防措施 1. **添索引优化**: 对 `WHERE/JOIN` 涉及的列创建索引 2. **资源管控**: 使用 Oracle Resource Manager 限制用户资源组 3. **监控工具**: 使用 `v$session_longops` 监控长时操作: ```sql SELECT sid, opname, time_remaining FROM v$session_longops WHERE time_remaining > 0; ``` > **关键点**:当GUI工具中断失效时,**数据库层面的会话终止(`ALTER SYSTEM KILL SESSION`)是最直接有效的解决方案**(引用[4]锁处理思路)。对于持续性出现的问题,建议结合SQL优化和工具设置调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值