记录一次MySql死锁

表结构如下

 死锁语句如下,该语句的作用是根据偏移量与一个考场所安排的人数,来将这个考场的id赋给学生

 <update id="examForStudent">
        update studentinfo
        set exam_id = #{examId}
        where id in
              (select id
               from (select id
                     from studentinfo
                     where courseId = #{courseId}
                       and school_area = #{schoolArea}
                       and exam_id = -1 //表示未安排
                     ORDER BY id
                         limit #{offset}, #{count}) as t)
</update>

发生死锁的场景(对于一个课程,可能分布在不同的校区,为了提高安排的速度,根据不同校区开启多个线程,每个线程都会拿到一个ExamList(已经安排好的考场),根据这个List,循环执行上述的Sql)

//offset起始为0
for (Exam exam : examList) {//一个循环下 课程Id、校区都是一样的
            int studentCount = exam.getStudentCount();
            Integer examId = exam.getId();
            studentinfoMapper.examForStudent(examId, studentCount, offset, exam.getCourseid(), exam.getSchoolarea());
            //将学生数作为偏移量
            offset += studentCount;
}

最后发生了死锁。

分析(对锁的理解还不到位,后面再具体分析):

目前看来,就是update,会在where后面所涉及的行的主键加上共享锁,若是涉及到二级索引,会再二级索引以及主键上都加共享锁。。。。

解决:

分两步,第一步:查出所有Id,第二步:安排

存在的问题: 

1、若此时有id发生了变化

2、该位置的事务不能生效,因为没有被Spring的IOC管理

好处: 

1、比原来更高效 

2、不会发生死锁问题,因为多个线程下锁涉及的主键不会发生冲突(一个线程负责一个课程下一个校区的所有安排)

 if (examList == null || examList.size() <= 0) {
            return;
        }
        Exam exam0 = examList.get(0);
        String courseId = exam0.getCourseid();
        int schoolarea = exam0.getSchoolarea();
        //查出该课程、该校区下所有未安排学生的id
        List<Integer> list = studentinfoMapper.queryAllIdByCourseIdAndSchoolArea(courseId, schoolarea);
        for (Exam exam : examList) {
            int studentCount = exam.getStudentCount();
            Integer examId = exam.getId();

            List<Integer> subList = list.subList(offset, offset + studentCount);
            studentinfoService.update().setSql("exam_id = " + examId).in("id", subList).update();
            offset += studentCount;
        }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值