表结构如下:
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
`coupon` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '优惠券码',
`type` tinyint(1) unsigned NOT NULL COMMENT '类型',
`is_use` enum('N','Y') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'N' COMMENT '是否使用',
`use_date` int(10) unsigned NOT NULL COMMENT '使用时间',
`is_del` enum('N','Y') COLLATE utf8_unicode_ci NOT NULL DEFAULT 'N' COMMENT '是否删除',
`creation_date` int(10) unsigned NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `type` (`type`,`is_use`,`is_del`)
现在使用这样的查询,会锁表:
begin;
select * from coupon_list where is_use='N' and type=5 and is_del='N' limit 1 for update;
如何才能触发行锁而不是表锁?
1
abelyao 2015-03-16 13:13:22 +08:00
这是把所有优惠码都先创建好、然后去表里找没有使用的吗?
换个思路、在用户领取的时候再创建优惠码呢? |
2
c742435 2015-03-16 13:14:48 +08:00 1
不懂数据库。
但是为什么要先生成优惠码。为什么不在发放的时候向数据库存入优惠码。 |
3
zhicheng 2015-03-16 13:22:57 +08:00 via Android
用派发终端数做取余。
|
4
loading 2015-03-16 13:26:28 +08:00
发放时再取优惠码,根据 timestamp 计算 md5 就可以直接用,查找数据库,已有就重新取。
|
5
kran OP |
6
hackwjfz 2015-03-16 14:01:11 +08:00
加一个flag,发出去了从1变0.不知道这样可否。
|
7
zhicheng 2015-03-16 14:02:08 +08:00 via Android
比如有两个终端,编号为 0和1。
那么0号终端永远取,id % 2 == 0 的记录。1 号终端永远取 id % 2 == 1 的记录。依此类推。 |
9
kran OP @zhicheng
不太一样,这个是通过web发码,如果对客户端信息做一定的计算,应该是能降低取到同一条记录的概率,但还是会重复。 |
10
zhicheng 2015-03-16 14:16:45 +08:00 via Android
通过web发码也是要经过服务器的。
|
11
kran OP @zhicheng
我的理解是:根据终端号分配优惠码的区段(也就是一个码只会对应到一个终端),条件是终端数目是有限的,web的情况是终端数是无限的。不知道我理解的对不对? |
12
justlikemaki 2015-03-16 14:25:17 +08:00
对于取兑换码的这个过程写个同步可以么?
|
13
zhicheng 2015-03-16 14:28:07 +08:00 via Android
你要分清楚终端和顾客的区别,顾客是无限的,终端是有限的。比如你准备10台服务器抢优惠码,那这10台服务器就是10个终端。
|
14
kran OP |
15
fityme 2015-03-16 14:31:42 +08:00
我的做法是,给这批优惠码加一个索引存redis,依赖redis做锁,不锁数据库。
这样做的问题就是多依赖了一个点,然后虽然不会一个优惠码多发,但是异常情况可能会造成活动结束有些码没有真的发出去,细节可以再考虑一下。 |
16
laoyur 2015-03-16 14:32:21 +08:00
数据库那块不太精通
不过要我来做的话,我会想办法在应用层来处理这个资源竞争的问题,比如用semaphore之类 |
17
happywowwow 2015-03-16 14:32:23 +08:00
|
18
kran OP @zhicheng
嗯。。现在的情况是,一台服务器负责发码,每一个访问网站的人都可以*即时*得到一个优惠码。顾客是直连到服务器的。 |
20
zhicheng 2015-03-16 14:43:25 +08:00 via Android
那10个进程?
原来你只有一台服务器,who the fucking care.... 如果你的QPS不到10K,请直接锁库。如果QPS不到100K,请使用队列。我设计的是100K以上的需求。。。 |
22
kran OP @happywowwow 文章里说的是,使用相同索引那么就会对索引加锁,跟锁表差不多。
@zhicheng 对哦!进程数是有个大概范围的。我真是笨死了 T_T 看来这个场景下之需要所表就ok的。 能问一下100k以上需要那些处理吗? |
24
zhicheng 2015-03-16 14:53:55 +08:00 via Android 1
一个数据库肯定顶不住,在创建优惠码之后可以分布到多个数据库里,同时读写,等一批码发完最后同步一下。这种方式可以任凭你分布到多少服务器上都不会互相影响。
|
26
zhicheng 2015-03-16 15:02:37 +08:00 via Android
恭喜你掌握了每秒顶住10亿红包系统的秘密。。。
|
28
ayv 2015-03-16 15:16:27 +08:00
把优惠码放到一个持久化的队列 例如redis的list
|
29
happywowwow 2015-03-16 15:28:55 +08:00 1
”InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!“
|
30
lujjjh 2015-03-16 15:37:35 +08:00 1
如果一定要用 MySQL,为什么要设计成记录“优惠码是否使用”而不是记录“优惠码拥有者”呢?
先 SELECT 再 UPDATE 原子性难以得到保证的话,最简单的方案就是先 UPDATE 再 SELECT 嘛。 |
32
abelyao 2015-03-16 21:28:23 +08:00
@julyclyde 可没说临时生成就必须做重复判断哦,可以把 unique id 视作优惠码多一部分,算法有很多种,就看你怎么设计了。当然楼主说优惠码是已经有客户那边生成了,就不再讨论这个方案了。
|
33
lujiajing1126 2015-03-16 23:04:21 +08:00
@lujjjh select的时候加悲观锁
或者用version_code做乐观锁 |
34
lujiajing1126 2015-03-16 23:05:22 +08:00
话说为啥不试试redis的list。。。
很多抢票都是这么干的 |
35
donglingyongadls 2015-03-17 10:01:58 +08:00
借鉴bloom filter?没试过,仅供参考
|
36
ly827 2015-03-17 11:55:56 +08:00
貌似是新浪游戏的面试题啊~~可以尝试事务处理+for update~保证唯一 看情况决定用不用存储过程
|