面试官:MySQL如何实现分布式读写锁?,java反射面试题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

3、获取锁,过程如下

{

3.1、开启事务

3.2、int count = (update t_read_write_lock set w_count=1 where r_count = 0)

3.3、提交事务;

}

4、如果3.2的count==1,继续向下执行,否则终止业务

5、执行业务操作

6、释放锁,过程如下

{

6.1、开启事务

6.2、update t_read_write_lock set w_count=0 where w_count = 1

6.3、提交事务

}

整个过程有个问题,不知道大家发现没有,如果执行到 5 之后,系统挂了,会出现什么情况?

业务执行完毕了,但是 w 锁却没有释放,这种后果就是死锁了,以后 r 操作就没法执行了。

我们来看看,如何改进?

需要添加一下上锁日志表,每次上锁成功,则记录一条日志,表结构如下

create table t_lock_log(

id bigint primary key auto_increment comment ‘主键,自动增长’

resource_id varchar(50) primary key not null comment ‘互斥资源id’,

lock_type smallint default 0 comment ‘锁类型,0:W锁,1:R锁’,

status smallint default 0 comment ‘状态,0:获取锁成功,1:业务执行完毕,2:锁被释放’,

create_time bigint default 0 comment ‘记录创建时间’,

version bigint not null default 0 comment ‘版本号,每次执行update的时候+1’

);

如何使用呢?

下面看 W 过程的改进

1、通过resource_id去t_read_write_lock查询,如果不存在,则插入一条记录,这里由于resource_id是主键,所以对于同一个resource_id只会有一个插入成功,这里用 $lock_record表示t_read_write_lock记录

2、判断lock_record.w_count 0 && lock_record.r_count0,如果为true继续向下,否则返回false,业务终止

3、获取锁,过程如下

{

3.1、开启事务

3.2、int count = (update t_read_write_lock set w_count=1 where r_count = 0)

3.3、如果count==1,则插入一条上锁日志,锁类型是0,状态是0:insert t_lock_log (resource_id,lock_type,status,create_time) values (#{resource_id},0,0,‘当前时间’);

3.4、提交事务;

}

4、如果3.2的count==1,继续向下执行,否则终止业务

5、执行业务操作,业务操作过程如下

{

5.1、业务库开启事务

5.2、执行业务

5.3、更新锁日志记录的状态为1,条件中必须带上status=0:int updateLogCount = (update t_lock_log set status=1 where id=#{日志记录id} and status = 0)

5.4、if(updateLogCount==1){

5.5、提交事务

}else{

5.6、回滚事务【走到这里说明更新锁日志记录失败了,说明t_lock_log的status被其他地方改掉了,被防止死锁的job修改了】

}

}

6、释放锁,过程如下

{

6.1、开启事务

6.2、释放锁:update t_read_write_lock set w_count=0 where w_count = 1 and resource_id = #{resource_id}

6.3、更新锁日志状态为2:update t_lock_log set status=2 where id = #{日志记录id}

6.4、提交事务

}

2.3、死锁的处理

上面这个是正常流程,如果第 3 步执行完了,也就是上锁 W 锁成功,但是执行到第 6 步之前,系统挂了,此时 W 锁没有释放,会出现死锁。

此时我们需要一个 job,通过这个 job 来释放长时间还未释放的锁,比如过了 10 分钟,锁还未被释放的,job 的逻辑如下

1、获取10分钟之前锁未释放的锁日志列表:select * from t_lock_log where status in (0,1) and create_time+10分钟<=当前时

间的;

2、轮询获取的日志列表,释放锁,操作如下

{

2.1、开启事务

2.2、if(t_lock_log.lock_type==0){

//lock_type为0表示是W锁,下面准备释放W锁

//先将日志状态更新为2,注意条件中带上version作为条件,这里使用到了乐观锁,可以确保并发修改时只有一个count的值为1

int count = (update t_lock_log set status=2 where id = #{日志记录id} and version = #{日志记录.version})

if(count==1){

//将w_count置为0

update t_read_write_lock set w_count=0 where w_count = 1 and resource_id = #{resource_id}

}

}else{

//准备释放R锁

//先将日志状态置为2

int count = (update t_lock_log set status=2 where id = #{日志记录id} and version = #{日志记录.version})

if(count==1){

//将r_count置为r_count-1,注意条件中带上r_count - 1>=0

update t_read_write_lock set r_count=r_count-1 where r_count - 1>=0 and resource_id = #{resource_id}

}

}

2.3、提交事务

}

2.4、R 锁的过程

1、通过resource_id去t_read_write_lock查询,如果不存在,则插入一条记录,这里由于resource_id是主键,所以对于同一个resource_id只会有一个插入成功,这里用 $lock_record表示t_read_write_lock记录

2、判断lock_record.w_count ==0,如果为true继续向下,否则返回false,业务终止

3、获取锁,过程如下

{

3.1、开启事务

3.2、int count = (update t_read_write_lock set r_count=r_count+1 where w_count = 0)

3.3、如果count==1,则插入一条上锁日志,锁类型是1【表示R锁】,状态是0:insert t_lock_log (resource_id,lock_type,status,create_time) values (#{resource_id},1,0,‘当前时间’);

3.4、提交事务;

}

4、如果3.2的count==1,继续向下执行,否则终止业务

5、执行业务操作,业务操作过程如下

{

5.1、业务库开启事务

5.2、执行业务

5.3、更新锁日志记录的状态为1,条件中必须带上status=0:int updateLogCount = (update t_lock_log set status=1 where id=#{日志记录id} and status = 0)

5.4、if(updateLogCount==1){

5.5、提交事务

}else{

5.6、回滚事务【走到这里说明更新锁日志记录失败了,说明t_lock_log的status被其他地方改掉了,被防止死锁的job修改了】

}

}

6、释放锁,过程如下

{

6.1、开启事务

6.2、释放锁:update t_read_write_lock set r_count=r_count-1 where r_count - 1 >= 0 and resource_id = #{resource_id}

6.3、更新锁日志状态为2:update t_lock_log set status=2 where id = #{日志记录id}

6.4、提交事务

}

3、总结


本文主要介绍了如何使用 mysql 来实现读写锁,如何防止死锁,重点就是 2 张表,锁表和日志表,2 个表配合一个 job,就把问题解决了。

大家可以将上面代码转换为程序,结合 spring 的 aop 可以实现一个通用的 db 读写锁,有兴趣的可以试试,有问题欢加我微信 itsoku 交流。

4、Spring 系列


  1. Spring 系列第 1 篇:为何要学 spring?

  2. Spring 系列第 2 篇:控制反转(IoC)与依赖注入(DI)

  3. Spring 系列第 3 篇:Spring 容器基本使用及原理

  4. Spring 系列第 4 篇:xml 中 bean 定义详解(-)

  5. Spring 系列第 5 篇:创建 bean 实例这些方式你们都知道?

  6. Spring 系列第 6 篇:玩转 bean scope,避免跳坑里!

  7. Spring 系列第 7 篇:依赖注入之手动注入

  8. Spring 系列第 8 篇:自动注入(autowire)详解,高手在于坚持

  9. Spring 系列第 9 篇:depend-on 到底是干什么的?

  10. Spring 系列第 10 篇:primary 可以解决什么问题?

  11. Spring 系列第 11 篇:bean 中的 autowire-candidate 又是干什么的?

  12. Spring 系列第 12 篇:lazy-init:bean 延迟初始化

  13. Spring 系列第 13 篇:使用继承简化 bean 配置(abstract & parent)

  14. Spring 系列第 14 篇:lookup-method 和 replaced-method 比较陌生,怎么玩的?

  15. Spring 系列第 15 篇:代理详解(Java 动态代理&cglib 代理)?

  16. Spring 系列第 16 篇:深入理解 java 注解及 spring 对注解的增强(预备知识)

  17. Spring 系列第 17 篇:@Configration 和@Bean 注解详解(bean 批量注册)

  18. Spring 系列第 18 篇:@ComponentScan、@ComponentScans 详解(bean 批量注册)

  19. Spring 系列第 18 篇:@import 详解(bean 批量注册)

  20. Spring 系列第 20 篇:@Conditional 通过条件来控制 bean 的注册

  21. Spring 系列第 21 篇:注解实现依赖注入(@Autowired、@Resource、@Primary、@Qulifier)

  22. Spring 系列第 22 篇:@Scope、@DependsOn、@ImportResource、@Lazy 详解

  23. Spring 系列第 23 篇:Bean 生命周期详解

  24. Spring 系列第 24 篇:父子容器详解

  25. Spring 系列第 25 篇:@Value【用法、数据来源、动态刷新】

  26. Spring 系列第 26 篇:国际化详解

  27. Spring 系列第 27 篇:spring 事件机制详解

  28. Spring 系列第 28 篇:Bean 循环依赖详解

  29. Spring 系列第 29 篇:BeanFactory 扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)

  30. Spring 系列第 30 篇:jdk 动态代理和 cglib 代理

  31. Spring 系列第 31 篇:aop 概念详解

  32. Spring 系列第 32 篇:AOP 核心源码、原理详解

  33. Spring 系列第 33 篇:ProxyFactoryBean 创建 AOP 代理

  34. Spring 系列第 34 篇:@Aspect 中@Pointcut 12 种用法

  35. Spring 系列第 35 篇:@Aspect 中 5 中通知详解

最后

权威指南-第一本Docker书

引领完成Docker的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解Docker适用于什么场景。并且这本Docker的学习权威指南介绍了其组件的基础知识,然后用Docker构建容器和服务来完成各种任务:利用Docker为新项目建立测试环境,演示如何使用持续集成的工作流集成Docker,如何构建应用程序服务和平台,如何使用Docker的API,如何扩展Docker。

总共包含了:简介、安装Docker、Docker入门、使用Docker镜像和仓库、在测试中使用Docker、使用Docker构建服务、使用Fig编配Docke、使用Docker API、获得帮助和对Docker进行改进等9个章节的知识。

image

image

image

image

关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
1)]

[外链图片转存中…(img-ldzgy3Xy-1713383554701)]

[外链图片转存中…(img-O5yaidjg-1713383554701)]

[外链图片转存中…(img-pgayBQw3-1713383554702)]

关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-yM07EKAh-1713383554702)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值