数据库一般都会并发执行多个事务,多个事务可能会并发的对相同的一批数据进行增删改查操作,可能就会导致我们说的脏写、脏读、不可重复读、幻读这些问题.这些问题的本质都是数据库的多事务并发问题,为了解决多事务并发问题,数据库设计了事务隔离机制、锁机制、MVCC多版本并发控制隔离机制 , 用一整套机制来解决多事务并发问题.
1.事务及其ACID属性:
原子性(Atomicity):事务是一个原子操作单元,对数据的修改要么全部成功,要么全部失败.
一致性(Consistent):在事务开始和结束的时候,数据状态保持一致.
隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务不受外部并发操作影响.
持久性(Durable):事务完成以后,对数据的修改是永久性的,即使出现故障也能保持.
2.并发事务处理产生的问题.
2.1更新丢失或脏写:
当两个或者多个事务选择同一行数据进行修改.由于每个事务都不知道其他事务的存在,就会存在更新丢失,最后的更新覆盖了其他事务的更新.
2.2脏读:
事务A读取到了事务B还未提交的数据,然后在事务B的基础上并进行修改,如果事务B事务回滚,事务A的修改无效.不能够保持一致性.
2.3不可重复读:
一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了.这种现象就叫做“不可重复读”.
2.4幻读:
事务A读取到了事务B提交的新增的数据.不符合隔离性.
3.事务隔离级别:
数据库的隔离级别越高,并发带来的问题越少,但是效率就越低.
3.1查看当前数据库的隔离级别:
数据库的隔离级别越高,并发带来的问题越少,但是效率就越低.
3.1查看当前数据库的隔离级别:
3.2设置事务隔离级别:
4.锁详解:
锁是计算机协调多个进程或者线程并发访问某些资源的机制.在数据库中,除了传统的计算资源(cpu ram i/o等)争用以外.数据也是用户需要争用的资源.
保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素.
4.1锁分类:
从性能上分为乐观锁(版本对比来实现)和悲观锁.
数据库操作类型上分为读锁和写锁(都属于悲观锁).
读锁:针对同一份数据,多个读操作可以同时进行,不会互相影响.
写锁:当前写操作完成以前,会阻断其他读操作和写操作.
对数据操作粒度分为表锁和行锁.
表锁:
每次操作锁住整张表。开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低;一般用在整表数据迁移的场景.
4.2手动增加表锁:
lock table 表名称 read ( write ),表名称2 read **( **write ).
查看加过的锁:
show open tables.
删除表锁:
unlock tables.
4.3写锁演示:
加锁的session可以正常操作.但是其他的session就会被阻塞.
4.4读锁演示:
释放写锁以后.
插入数据都成功了.
5.行锁:
每次操作锁住一行数据。开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高.
5.1行锁演示:
开启了一个事务,然后另一边进行修改同一条数据,会发现被阻塞.
当提交事务以后,另一边就会修改成功.
5.2行锁总结:
一个session开启事务更新不提交,另一个session更新同一条记录会阻塞,更新不同记录不会阻塞.
MyISAM在执行查询语句SELECT前,会自动给涉及的所有表加读锁,在执行update、insert、delete操作会自动给涉及的表加写锁。
InnoDB在执行查询语句SELECT时(非串行隔离级别),不会加锁。但是update、insert、delete操作会加行锁。
读锁会阻塞写,但是不会阻塞读。而写锁则会把读和写都阻塞.
6.表结构和数据.
CREATE TABLE `actor` (
`id` int(11) NOT NULL,
`name` varchar(45) DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `db7`.`actor`(`id`, `name`, `update_time`) VALUES (1, 'a', '2025-06-08 10:33:12');
INSERT INTO `db7`.`actor`(`id`, `name`, `update_time`) VALUES (2, 'b', '2025-06-08 15:27:18');
INSERT INTO `db7`.`actor`(`id`, `name`, `update_time`) VALUES (3, 'c', '2025-06-08 15:27:18');
INSERT INTO `db7`.`actor`(`id`, `name`, `update_time`) VALUES (4, 'd', '2025-06-08 10:33:12');
INSERT INTO `db7`.`actor`(`id`, `name`, `update_time`) VALUES (5, 'd', '2025-06-08 10:44:12');
没了解过mysql的锁机制的时候,总是停留在嘴上,表锁行锁.了解了java的内置锁和显示锁以后,在看着这个.底层都是贯通的.oh,数据库的锁,可能就是这或者那样的.然后好奇心会驱使你再去研究c语言.哈哈哈.可能这就是坚持的乐趣吧.
如果大家喜欢我的分享的话,可以关注我的微信公众号
念何架构之路