mysql常用的引擎,锁,幂等处理
作者:Vic ,分类:Mysql 发布于 2021-10-24 03:42:46

常用引擎特点

  • InnoDB
    • mysql目前的默认引擎 , 支持ACID事务 , 支持行级锁表级锁 , 支持外键 , 总条数统计会扫码全表, update和insert操作需求量大使用该引擎最优 .
  • MyISAM
    • 支持表级锁, 查询性能优 , 无条件总条数统计有个变量存储无需扫描全表. 其读操作不会阻塞其他读操作 但会阻塞其他写操作 , 写操作会阻塞其他读操作和写操作, 通过语句lock table 表名 read | wirte; 用来锁定读或写操作, 使用unlock tables; 解锁 , 这种方式会产生死锁 , 通过事务commit或设置默认超时时间自动断开来解决

悲观锁

每次对数据操作前 , 防止其他连接修改数据 , 直接上锁 , 操作完成后在释放锁 , 期间其他连接无法读数据修改 . 比如表锁 , 行锁等等.

乐观锁

每次对数据操作前 , 不会担心有其他连接修改数据 , 通过自定义的版本号字段或状态字段就可以实现或者lock in share mode

for update 独占锁

  • 加锁需要的环境
    • sql测试的话 , 加锁语句必须在begin;和commit;之间
    • java测试的话 , 需要设置connection.setAutoCommit(false); 手动控制commit和rollback
  • 加锁成功时锁的级别
    • where 列条件 fro update :
      • 列条件 是主键,普通索引,唯一索引,组合索引或者 索引范围查询 (>,<,in, not in语句)内的话锁的级别为行级锁, 只锁定条件内的数据 , 其他事务操作该条件内的数据会阻塞 , 直至锁释放后执行. 细心的你可能会问 , 如果列条件 内同时有索引列和普通列是什么级别? 这个我也测试了也是行级别锁 , 索引是like语句是表级锁
      • 列条件 是普通列 , 级别为表级锁, 会锁住全表数据 , 其他事务直至该锁释放后执行
      • 上述两点mysql 8.0环境下亲测有效, 其他版本不保证

lock in share mode 共享锁

  • 加锁需要的环境for update独占锁一致
  • 加锁成功时锁的级别
    • where 列条件 lock in share mode :
      • 级别同for update独占锁一致 , 区别是如果事务A对条件数据加上共享锁之后 就可以进行读写操作, 但其他事务可以对该对条件数据加共享锁,但不能加排他锁,且只能读数据,不能修改数据。 其他事物需要共享锁都commit后才能进行修改操作, 这种方式会产生死锁 , 通过事务commit或设置默认超时时间自动断开来解决

数据库级别的幂等处理

  • 解决方案
    • 状态字段 : 假如,订单表有支付状态payStatus字段 , 0代表未支付 , 1代表支付成功 , 2代表支付失败 , 3代表取消支付 , 假如订单A已经支付成功了payStatus为1 , mysql这时候又接到一个订单A支付成功的payStatus=1更新请求 , 为了不对现有订单payStatus有影响, 直接set payStatus=1 where order_no=xxxx and payStatus=0 , 按照订单业务状态正向流转的顺序就是必须从未支付到支付成功才算合理 , 假如第一次支付失败了 , 用户再发起一次 , 改进下就是set payStatus=1 where order_no=xxxx and payStatus=0 or payStatus=2

基于业务层级别的幂等处理

  • 当然实际项目下幂等的操作不会交给数据库 , 毕竟那样数据库的压力也大 , 这样的逻辑处理应该先在后端逻辑层就拦截下来了
    • 基于Redis或zookeeper等 支付接口用分布式锁以用户ID+订单号维度锁住 , 判断订单状态是不是payStatus=0 or payStatus=2 , 是的话在进行payStatus=1操作 , 最后释放锁.