MYSQL面试--并发一致性问题+锁机制(二)

-- 3.并发一致性问题(面试经常考)
-- 丢失修改:关键词:修改&&覆盖
	-- 两个事物并发对一个变量进行'修改',A先update结果,B后update覆盖了A结果,A修改丢失
-- 脏读:关键词:读取&&回滚
	-- 事物A更新数据后回滚前,事物B读取了回滚前的数据,又称无效数据读出
-- 不可重复读:关键词:读两次&&修改
	-- 同一个事务两次读的数据不一样,因为中间B修改了数据
-- 幻读:读两次(记录条数)&&增删
	-- 事务A两次读取记录条数不一样,因为中间B增删了数据

-- 4.锁机制:
-- 4.1锁对比
-- 资料1:https://blog.csdn.net/puhaiyang/article/details/72284702    -- 锁对比
-- 资料2:https://baijiahao.baidu.com/s?id=1610581108528334819&wfr=spider&for=pc    -- 锁详解
-- 资料3:http://www.runoob.com/mysql/mysql-transaction.html    -- mysql事务语法
-- 资料4:https://www.cnblogs.com/dplearning/p/6394527.html    -- rollback无效原因
-- 资料5:https://www.cnblogs.com/lqCnblog/p/6923217.html    -- 添加索引
-- 资料6:https://www.cnblogs.com/sos-blue/p/6852945.html    -- 创建用户授权
-- 新建账号:u:cy p:123

-- 乐观锁:乐观地认为这次操作不会咋胡,操作数据不加锁,更新后再判断有无冲突;乐观锁需要自己实现
	-- 如何实现乐观锁:给数据表增加version字段,每操作一次,将version记录+1
	-- 实例:更新一条数据,第一步先select查询出这条数据的version值1,第二步执行update时,where条件除了写id = xx 还要and version = 1;第三步version+1
	-- ps:除了手动实现,hibernate也实现了乐观锁
-- 悲观锁:悲观地认为该操作肯定咋胡,每次操作都要获取锁才能操作数据;mysql已经实现
	-- 读锁(共享锁,行级锁定)
		-- 在执行语句后面加上lock in share mode就代表对某些资源加上共享锁了
		
		-- 例子:测试共享锁、排它锁代码(需要另起账号哦)
		SET AUTOCOMMIT=0 ; -- 禁止自动commit,手动测试可以开;平时设置1自动提交比较方便	
		START TRANSACTION;-- begin/START TRANSACTION 显式开启事务
		
		UPDATE demo SET NAME = 'bb' WHERE id = 1;
		INSERT INTO demo VALUES('libai','456',4); -- 插入语句
		-- delete from demo where name='libai'; -- 删除语句
		SELECT * FROM demo WHERE id = 1 LOCK IN SHARE MODE; -- 共享锁,共享锁的测试需要新建另一个连接(两个用户竞争)才能测试,新建会话是没用的的~
		ALTER TABLE demo ADD PRIMARY KEY(id);	-- 添加索引
		
		ROLLBACK; -- 回滚事务
		COMMIT; -- 提交事务
		
	-- 写锁(排它锁,行级锁定)
		UPDATE demo SET NAME = 'qq' WHERE id = 1;-- 排它锁,update、insert、delete自动加排它锁
		SELECT * FROM demo WHERE id = 1 FOR UPDATE -- 排他锁,select ... for update
	-- 意向共享锁(表级锁定):除排它锁,其他锁都兼容(兼容意思是加两个锁不会堵塞)
	-- 意向排它锁(表级锁定):跟共享锁排它锁冲突,其他锁兼容
	
	-- 规则1:读锁读锁共存;读锁写锁排斥;写锁写锁排斥
	-- 规则2:如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。
	-- 其实容易理解,读数据不会造成并发问题,写数据会,读写一起容易脏读不可重复读;读读一起容易丢失修改
	
-- 行锁:字面意思,锁一行上面lock in share mode 也算是行锁,会死锁,以并发更新并发查询为主;储存引擎innodb就是行锁(查询where是索引才时,如果where用普通属性找数据,则是表锁)
-- 页锁:字面意思,锁定一个页面,比较少见,跟行锁一样,会死锁
-- 表锁:字面意思,锁一个表,可以防止死锁,以查询为主;储存引擎mylsam就是表锁
	SHOW OPEN TABLES;-- 显示锁/非锁表
	LOCK TABLE demo WRITE,article READ;-- 锁表
	UNLOCK TABLE;-- 解锁全部表
-- 间隙锁:使用了范围><的时候,会对在范围中不存在的数据行加锁,导致不能改行不能插入,不如我有1245行数据,给x>1且x<5的加锁,另一边要插入x=3是阻塞的

-- 行锁优化建议
	-- 查看系统内部锁的使用情况
	SHOW STATUS LIKE 'table%';
	-- 缩短锁定时间
		-- a)尽量减少大的复杂Query,将复杂Query分拆成几个小的Query分布进行;
		-- b)尽可能的建立足够高效的索引,让数据检索更迅速;
		

-- 总结:锁定资源的颗粒度越小,锁定相同数量数据消耗的内存就越多,实现算法越复杂,但请求遇到锁等待的情况也会减少,并发度提高

-- 疑问1:(2)由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。



-- 4.2.死锁:资料:https://www.cnblogs.com/sivkun/p/7518540.html
-- 第一种情况:用户A查询表A(共享锁A)然后访问B表,用户B查询查询表B(共享锁B)然后访问A,这样AB都在等待对方释放表才能继续,死锁产生
-- 第二种情况:用户A查询一条记录(共享锁A),然后修改这条记录(排它锁A);这时,用户B修改这条记录(排它锁B);B的独占锁等待A的共享锁释放,A由于B的独占锁无法继续升级排它锁所以无法释放共享锁,死锁产生
-- 第三种情况:事务执行了一条不满足条件的update语句,则执行全表扫描,把行级锁升级为表级锁,多个这样的事务执行容易产生死锁和堵塞,死锁产生
-- 解决方法在资料链接~~:关键词:调整程序逻辑,避免同时锁定两个资源;使用乐观锁悲观锁;sql语句少关联多表查询,使用explain优化sql语句,对全表扫描sql语句,建立索引优化


-- 死锁例子
-- 错误代码: 1213
-- Deadlock found when trying to get lock; try restarting transaction

猜你喜欢

转载自blog.csdn.net/qq_38056704/article/details/82807132
今日推荐