Innodb锁的一点小分享

发生场景

大佬帮忙review代码的时候,看到我得写的sql,就给我指正下。

$coupon = EnterpriseCoupon::whereId($params['coupon_id'])
    ->where('scope_id', $params['scope_id'])
    ->lockForUpdate()
    ->first();
复制代码

因为mysql是短链接,mysql begin之后,如果发生异常,直接 throw 退出。不用 rollback 这样可以,因为mysql 链接是短链接,所以会主动的去释放这个锁的线程。

数据表

CREATE TABLE `kbb_common`.`enterprise_coupon` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `creator_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建人 admin_users.id',
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '优惠券名称',
  `introduce` varchar(255) DEFAULT '' COMMENT '优惠券描述',
  `introduce_info` varchar(255) DEFAULT '' COMMENT '优惠券描述详情',
  `type` int(2) unsigned NOT NULL DEFAULT '1' COMMENT '优惠券类型 1:满减消费券',
  `over_amount` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '满多少金额可用(元)',
  `discount_fee` decimal(10,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '优惠券抵扣金额(元)',
  `effective_date` date DEFAULT NULL COMMENT '固定生效日期',
  `expired_date` date DEFAULT NULL COMMENT '固定过期日期 ',
  `status` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '状态 0 待审核 1正常 2 下架',
  `remarks` varchar(255) DEFAULT '' COMMENT '备注(仅后台可见)',
  `created_at` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `total_num` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '发券数量',
  `use_channel` int(10) unsigned DEFAULT '0' COMMENT '使用场景,1101:酒店;1102:外卖;1103:打车;1104:机票;1105:加油;1106:火车票;',
  `superposition` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '是否叠加 0不可叠加 1叠加 ',
  `used_num` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '优惠劵已用数量',
  `corp_name` varchar(255) DEFAULT '' COMMENT '企业名称',
  `scope` int(10) unsigned NOT NULL DEFAULT '1' COMMENT '作用域 1 指定企业,2指定地区',
  `scope_id` int(10) DEFAULT NULL COMMENT '作用域id 如果是指定企业,就是企服的企业id',
  PRIMARY KEY (`id`),
  KEY `idx-name` (`name`),
  KEY `idx-corp_id` (`scope_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COMMENT='企业优惠券表';
复制代码

insert 语句

INSERT INTO `kbb_common`.`enterprise_coupon`(`id`, `creator_id`, `name`, `introduce`, `introduce_info`, `type`, `over_amount`, `discount_fee`, `effective_date`, `expired_date`, `status`, `remarks`, `created_at`, `updated_at`, `total_num`, `use_channel`, `superposition`, `used_num`, `corp_name`, `scope`, `scope_id`) VALUES (1, 15, '100元优惠卷', '100元优惠卷', '', 1, 2000.00, 100.00, '2021-09-03', '2021-09-15', 1, '100元优惠卷', '2021-09-03 15:32:14', '2021-09-14 11:03:14', 100, 1101, 0, 8, '', 1, 10);
INSERT INTO `kbb_common`.`enterprise_coupon`(`id`, `creator_id`, `name`, `introduce`, `introduce_info`, `type`, `over_amount`, `discount_fee`, `effective_date`, `expired_date`, `status`, `remarks`, `created_at`, `updated_at`, `total_num`, `use_channel`, `superposition`, `used_num`, `corp_name`, `scope`, `scope_id`) VALUES (2, 15, '200元优惠卷', '200元优惠卷', '', 1, 2000.00, 5000.00, '2021-09-03', '2021-09-16', 1, '100元优惠卷', '2021-09-03 15:35:47', '2021-09-03 15:35:47', 200, 1102, 0, 2, '', 1, 1);
INSERT INTO `kbb_common`.`enterprise_coupon`(`id`, `creator_id`, `name`, `introduce`, `introduce_info`, `type`, `over_amount`, `discount_fee`, `effective_date`, `expired_date`, `status`, `remarks`, `created_at`, `updated_at`, `total_num`, `use_channel`, `superposition`, `used_num`, `corp_name`, `scope`, `scope_id`) VALUES (3, 15, '300元优惠卷', '300元优惠卷', '300元优惠卷', 1, 2000.00, 200.00, '2021-09-03', '2021-09-08', 1, '100元优惠卷', '2021-09-03 15:48:53', '2021-09-08 16:06:43', 200, 1101, 0, 200, '', 1, 1);
INSERT INTO `kbb_common`.`enterprise_coupon`(`id`, `creator_id`, `name`, `introduce`, `introduce_info`, `type`, `over_amount`, `discount_fee`, `effective_date`, `expired_date`, `status`, `remarks`, `created_at`, `updated_at`, `total_num`, `use_channel`, `superposition`, `used_num`, `corp_name`, `scope`, `scope_id`) VALUES (4, 15, '500元优惠卷', '500元优惠卷', '500元优惠卷', 1, 2000.00, 500.00, '2021-09-03', '2021-09-08', 1, '100元优惠卷', '2021-09-06 11:19:35', '2021-09-08 16:24:16', 500, 1101, 0, 0, '', 1, 1);
INSERT INTO `kbb_common`.`enterprise_coupon`(`id`, `creator_id`, `name`, `introduce`, `introduce_info`, `type`, `over_amount`, `discount_fee`, `effective_date`, `expired_date`, `status`, `remarks`, `created_at`, `updated_at`, `total_num`, `use_channel`, `superposition`, `used_num`, `corp_name`, `scope`, `scope_id`) VALUES (5, 15, '500元优惠卷', '500元优惠卷', '500元优惠卷', 1, 2000.00, 500.00, '2021-09-03', '2021-09-08', 1, '100元优惠卷', '2021-09-06 17:58:39', '2021-09-08 16:09:45', 2500, 1101, 0, 1, '高灯科技', 1, 1);
INSERT INTO `kbb_common`.`enterprise_coupon`(`id`, `creator_id`, `name`, `introduce`, `introduce_info`, `type`, `over_amount`, `discount_fee`, `effective_date`, `expired_date`, `status`, `remarks`, `created_at`, `updated_at`, `total_num`, `use_channel`, `superposition`, `used_num`, `corp_name`, `scope`, `scope_id`) VALUES (6, 15, '500元优惠卷', '500元优惠卷', '500元优惠卷', 1, 2000.00, 500.00, '2021-09-03', '2021-09-08', 2, '100元优惠卷', '2021-09-06 18:01:04', '2021-09-06 18:01:04', 2500, 1101, 0, 0, '高灯科技', 1, 1);

复制代码

发生锁等待场景

-- session 1
begin;
select * from enterprise_coupon where status = 1

---等待
复制代码
-- session 2
update enterprise_coupon set remakrs = 1 where status =2 ;
这个时候这条sql就会一直等待,不执行,最后执行超时。

复制代码

产生的原因

innodb 的行锁是通过给索引上的索引项加锁实现的。
当我们 where status = 1 ,status 我们我们没有建立索引,所以,就会进行全表扫描。那么就是全表的数据遍历出来后,都加上了锁。这么以来,你修改人设数据都会阻塞。要等这个sql commit 才行。

注意事项

所以在使用锁的时候,就要注意是否锁上有索引,没有索引这么就很危险。

MySQL锁机制,行锁竟然加在索引上

查看锁

show processlist 查看所有数据库状态
select * from information_schema.innodb_trx 查看事物进程
select * from information_schema.innodb_locks 查看锁

查询正在执行的事务以及等待锁 常用的sql语句

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享