本文总结数据库事务
、安全问题与隔离级别
,以及数据库连接池
的相关知识。
一、MySQL事务(Transaction)
MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务。
MYSQL 事务处理主要有两种方法:
1、用 BEGIN, ROLLBACK, COMMIT来实现
1
2
3
4BEGIN 开始一个事务
//START TRANSACTION
ROLLBACK 事务回滚
COMMIT 事务确认2、直接用 SET 来改变 MySQL 的自动提交模式:
1
2SET AUTOCOMMIT=0 禁止自动提交
SET AUTOCOMMIT=1 开启自动提交
在JDBC代码中,需要有两个声明:1
2
3
4
5
6
7
8
9//调用Connection对象的setAutoCommit();
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/database_name", "user", "password");
conn.setAutoCommit(false);
//随后执行数据库操作
...
//最后提交
conn.commit()
//并且在异常处理中回滚
conn.rollback();
注意:
setAutoCommit()只针对Connection
对象,如果新建一个连接对象,仍然是默认提交。
二、事务的特性
事务是必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
三、数据库的安全问题和隔离级别
(1)安全问题-读
脏读、 不可重复读、幻读
脏读:一个事务读到了另一个事务未提交的数据。
不可重复读:一个事务读到了另一个事务提交的数据,多次查询结果不一致。
幻读:一个事务读到了另一个事务已提交的插入的数据,导致多次查询结果不一致。
(2)安全问题-写
丢失更新问题,指一个事务去修改数据库, 另一个事务也修改数据库,最后的那个事务,不管是提交还是回滚都会造成前面一个事务的数据更新丢失
主要解决办法是加锁:乐观锁、悲观锁。
悲观锁:指事务在一开始就认为丢失更新一定会发生, 这是一件很悲观的事情。类似synchronized加锁。悲观锁通过在查询条件后面加
for udate
实现。乐观锁:指从来不会觉得丢失更新会发生。通过手动在数据库中设置字段,利用
CAS
操作实现。
(3)不可重复读和幻读的区别
幻读更加关注于insert操作,事务在插入已经检查过不存在的记录时,惊奇的发现这些数据已经存在了,之前的检测获取到的数据如同鬼影一般。
(4)隔离级别-四种
读未提交:Read Uncommitted
引发问题: 脏读
读已提交:Read Committed
解决: 脏读 , 引发: 不可重复读
可重复读:Repeatable Read
解决: 脏读 、 不可重复读 , 未解决: 幻读
可串行化:Serializable
解决: 脏读、 不可重复读 、 幻读。
mySql
默认的隔离级别是 可重复读Oracle
默认的隔离级别是 读已提交
(5)设置隔离级别
1 | //读未提交 |
四、数据库连接池
(1)作用
1. 资源重用
由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
2. 更快的系统响应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
3. 新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
4. 统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。一个最小化的数据库连接池实现:
(2)常用数据库连接池
常用的数据库连接池包括DBCP
,C3P0
,Druid
。
关键配置:
最小连接数:
是数据库一直保持的数据库连接数,所以如果应用程序对数据库连接的使用量不大,将有大量的数据库资源被浪费。
初始化连接数:
连接池启动时创建的初始化数据库连接数量。
最大连接数:
是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求被加入到等待队列中。
最大等待时间:
当没有可用连接时,连接池等待连接被归还的最大时间,超过时间则抛出异常,可设置参数为0或者负数使得无限等待(根据不同连接池配置)。
A. DBCP
使用
1 | public void testDBCP01(){ |
B.C3P0
使用
1 | Connection conn = null; |
注意,在使用c3p0连接池的时候,可能遇到无法连接的问题,而此时,可以尝试将配置文件
c3p0-config.xml
改为如下内容:
1 | <c3p0-config> |
(3)自定义数据库连接池
使用栈来存放数据库连接,每次都从上面取出连接,使用完也放回上面。假如使用的频率特别低会导致栈底部的连接长时间未使用,则可以直接释放以节省资源。
连接容器中超时连接的释放有两种方式:
(1)在往容器中添加或者取出连接的时候释放。
(2)单独开一个线程不断轮询所有连接释放超时的连接。一般采用第一种方式。栈中连接的使用时间是有序的。所以每次释放的时候,只需要从底部向上开始扫描,遇到超时的连接则进行释放,遇上非超时的连接则停止扫描,如果栈中连接均未超时,则只需要扫描最后一个就可以了。