NoteDeep

mysql 秒杀的策略以及思考

【扩容】【静态化】【限流】
  A:扩容
  加机器,这是最简单的方法。
  B:静态化
  将活动页面上的所有可以静态的元素全部静态化,并且通过CDN加速
  C:限流
  一般都会采用IP级别的限流,即针对某一个IP,限制单位时间内发起请求数量。
后端的数据库在高并发和超卖下会遇到什么问题呢?
主要会有如下3个问题:(主要讨论写的问题,读的问题通过增加cache可以很容易的解决)
  I: 一般来说,MySQL的处理性能会随着并发thread上升而上升,但是到了一定的并发度之后会出现明显的拐点,之后一路下降,最终甚至会比单thread的性能还要差。
  II: 其次,超卖的根结在于减库存操作是一个事务操作
  III:最后,当减库存和高并发碰到一起的时候,由于操作的库存数目在同一行,就会出现争抢InnoDB行锁的问题,导致出现互相等待甚至死锁,从而大大降低MySQL的处理性能。
错误的方法:
<?
// beginTranse(开启事务)
try{
select count from s_store where postID = 12345;
if(count > 0){
update s_store set count = count - 1 where postID = 12345;
insert into buy_log (userID, postID) VALUES (22, 12345);
}
}catch($e Exception){
// rollBack(回滚)
}
// commit(提交事务)
  • count 可能会变为 -1
  • 可能会插入多条购买的记录

乐观锁:

//beginTranse(开启事务)
try{
affect_rows = update s_store set count = count - 1 where count>=1 and postID = 12345;
if (affect_rows === 1) {
insert into buy_log (userID, postID) VALUES (22, 12345);
}
}catch($e Exception){
rollBack(回滚)
}
// commit(提交事务)

或者这样:
<?
//beginTranse(开启事务)
try{
count = select count from s_store where postID = 12345;
if (count > 0) {
affect_rows = update s_store set count = count - 1 where count=count and postID = 12345;
if (affect_rows === 1) {
insert into buy_log (userID, postID) VALUES (22, 12345);
}
}
}catch($e Exception){
rollBack(回滚)
}
// commit(提交事务)

悲观锁:

select … for update.
事务给rows上排他锁,其他事务必须排队等待。相当于在数据库层面串行化了。显而易见的是随着并发量的增大,事务处理的性能越差。
BEGIN;
count = SELECT count FROM s_store WHERE postID = 12345 AND count>0 FOR UPDATE;
if (count > 0) {
affect_rows = UPDATE s_store SET count=count-1 WHERE postID = 12345 AND count> 0;
if (affect_rows === 1) {
insert into buy_log (userID, postID) VALUES (22, 12345);
}
}
COMMIT;
优化悲观锁的方法:核心是各个层面做好排队即可,如:
  • innodb_thread_concurrency 在InnoDB存储引擎层做“限流”
通过innodb_thread_concurrency参数来控制InnoDB存储引擎层的并发量。将innodb_thread_concurrency设置为16,性能的确会有一定的提升。并发线程数在128的时候,TPS从原有的4300提升为了7200,将近有65%的性能提升。但是在256线程之后,性能依旧堪忧。
  • mysql连接线程池的大小设置为16 在mysql连接池处限制并发数。
性能又获得了进一步的提升。
但是线程池这里有个参数thread_pool_oversubscribe,这个参数其实有点类似云计算中“超售”概念,即MySQL的线程池允许有额外的线程运行。该参数默认是3,之前thread_pool_size设置为16,那么总共允许16*(1+3)=64个线程同时运行。
这个参数的默认值本身没有问题,但是对于秒杀应用来说确是不需要的,因为之前已经讨论过,秒杀应用是串行的。所以将参数thread_pool_oversubscribe设置为1,秒杀还能有进一步的提升。

评论列表

    mysql 秒杀的策略以及思考
    乐观锁:
    悲观锁: