并发场景下常见的锁及简单使用

  1. MySQL悲观锁

    悲观锁:顾名思义,对待过来的请求持比较悲观的态度,在处理请求的整个过程中,将数据锁定,不允许其他进程/线程 修改

    set autocommit=0;
    begin;
    select * from table where id = xx for update; //互斥锁      //InnoDB 必须明确索引字段的值(查询需走索引),否则会将整个表的数据都加锁
    ......
    // 对数据进行操作
    ......
    commit;
    

    当session1执行完 select * from table where field = xx for update; (field需要是索引字段) 的时候 就将那一行的数据锁定了,此时 session2 再来执行 for update 或者 修改此条数据的操作的时候就会被阻塞

    注:MySQL 悲观锁 虽然数据可以保证绝对正确,但是并发效率极低,一般不使用

  2. MySQL乐观锁

    乐观锁:顾名思义,对待过来的请求持比较乐观的态度,先假设不会冲突,在提交更新的时候再去检验数据有没有被其他进程修改过,如果中间有被其他进程修改过起冲突了,则返回错误

    乐观锁的实现

    1. 版本控制 感兴趣的话可以看一下MySQL的MVCC的实现原理(InnoDB默认的可重复读隔离级别)

      MVCC 原理 大致是这样的:

      1. 数据库有隐藏的创建版本和删除版本的字段,每次开始事务的时候,事务版本号都会自增
      2. 新增数据的时候,在对应的创建版本号的地方填上 当前事务的版本号
      3. 更新数据的时候,将要更新的数据行的删除版本号填上当前事务的版本号,然后插入一条新数据,创建版本号 填上 当前事务的版本号
      4. 删除数据的时候,将要删除的数据行的删除版本号填上当前事务的版本号
      5. 查询数据的时候,查询 删除版本号大于当前事务的版本号 或 创建版本号小于等于当前事务的版本号且删除版本号为空
    2. 在where 条件中进行限制

      // 在秒杀场景中的使用
      1. 将库存字段设置为 unsigned int 类型,库存一直减,减到负数就直接报错,应用程序捕获这个错误进行处理这种方式依赖数据库抛异常,算是数据库设计的一种技巧,不算乐观锁
      2. 更新库存的时候,直接 update table set remain_amount = remain_amount - num where id = xx and remain_amount >= num							// num 是一个
      3. 结合 1,2
      
  3. Redis 分布式锁 参考 石杉的架构笔记--Redis分布式锁的实现原理

    1. 原理大概是这样的(Redis可能是一个集群,这里就当做是单机的场景,集群的话 加锁只给master节点加锁):

      1. Redis 是单进程单线程的,不涉及到锁的问题,至于Redis的并发是借助 I/O多路复用( Epoll)实现
      2. 请求进来后先检查 resource_str 这个hash类型的key存在不,如果不存在,则 hset resource_str client_str 1 ,然后设置一个过期时间;如果resource_str 存在,则 判断 resource_str client_str 存在不,如果存在,延长过期时间;如果不存在则获取一下 resource_str 的过期时间 time,然后client 进入循环等待time 秒 再重新 执行2 这个过程。
      3. 释放锁的时候,直接将 resource_str key 删除即可

      注:1. resource_str代表资源key client_str 代表客户端字符串或者是session字符串
            2. 上述情况如果 time过长,可以直接返回 超时
            3. 在redis master实例宕机的时候,可能导致多个客户端同时完成加锁

  4. Redis 分布式锁的改进

    1. 上述的Redis 分布式锁是基于一个 redis hash key 来实现的,这样相当于串行化,并发效果也不好,在 石杉的架构笔记--分布式锁高并发优化 中提到了一种办法,分段加锁。分段加锁相当于是把之前的一个大锁分成n个小锁,每个小锁管固定的一部分资源。当请求进来的时候就先利用3.1所述的Redis分布式锁原理选一个资源锁给当前客户端加上,然后判断商品库存满不满足,如果满足的话直接进行 创建订单、减库存、释放锁;如果不满足的话,直接释放当前的锁,再给客户端选下一个资源锁加上 (这个选资源锁的过程可以有一个随机的算法,但是需要保证尽量不重复)。当所有的资源锁都不满足的话,就只能返回库存不足了。
    2. 缺点:
      1. 实现比较复杂
      2. 如果每次下单的数量从一开始就一直大于 分段后的资源数,这种情况会到导致商品卖不完

注:1. 本文根据自己的理解所写,如有不对的地方请及时反馈。
      2. 如需转载,请注明出处:https://www.cnblogs.com/zhuchenglin/p/12694937.html

posted @ 2020-04-13 23:28  lin_zone  阅读(197)  评论(0编辑  收藏