redis技术的使用:
redis真的是一个很好的技术,它可以很好的在一定程度上解决网站一瞬间的并发量,例如商品抢购秒杀等活动。。。
redis之所以能解决高并发的原因是它可以直接访问内存,而以往我们用的是数据库(硬盘),提高了访问效率,解决了数据库服务器压力。
为什么redis的地位越来越高,我们为何不选择memcache,这是因为memcache只能存储字符串,而redis存储类型很丰富(例如有字符串、LIST、SET等),memcache每个值最大只能存储1M,存储资源非常有限,十分消耗内存资源,而redis可以存储1G,最重要的是memcache它不如redis安全,当服务器发生故障或者意外关机等情况时,redsi会把内存中的数据备份到硬盘中,而memcache所存储的东西全部丢失;这也说明了memcache不适合做数据库来用,可以用来做缓存。
引言
这里我们主要利用Redis的setnx的命令来处理高并发。
setnx 有两个参数。第一个参数表示键。第二个参数表示值。如果当前键不存在,那么会插入当前键,将第二个参数做为值。返回 1。如果当前键存在,那么会返回0。
创建库存表
1
2
3
4
5
|
CREATE TABLE `storage` ( `id` int (11) unsigned NOT NULL AUTO_INCREMENT, `number` int (11) DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1 |
设置初始库存为10
创建订单表
1
2
3
4
5
|
CREATE TABLE ` order ` ( `id` int (11) unsigned NOT NULL AUTO_INCREMENT, `number` int (11) DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1 |
测试不用锁的时候
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
$pdo = new PDO( 'mysql:host=127.0.0.1;dbname=test' , 'root' , 'root' ); $sql= "select `number` from storage where id=1 limit 1" ; $res = $pdo->query($sql)-> fetch (); $number = $res[ 'number' ]; if($number>0) { $sql = "insert into `order` VALUES (null,$number)" ; $order_id = $pdo->query($sql); if($order_id) { $sql= "update storage set `number`=`number`-1 WHERE id=1" ; $pdo->query($sql); } } |
ab测试模拟并发,发现库存是正确的。
1
2
3
4
5
6
7
|
mysql> select * from storage; + ----+--------+ | id | number | + ----+--------+ | 1 | 0 | + ----+--------+ 1 row in set (0.00 sec) |
在来看订单表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
mysql> select * from ` order `; + ----+--------+ | id | number | + ----+--------+ | 1 | 10 | | 2 | 10 | | 3 | 9 | | 4 | 7 | | 5 | 6 | | 6 | 5 | | 7 | 5 | | 8 | 5 | | 9 | 4 | | 10 | 1 | + ----+--------+ 10 rows in set (0.00 sec) |
发现存在几个订单都是操作的同一个库存数据,这样就可能引起超卖的情况。
修改代码加入redis锁进行数据控制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
<?php /** * Created by PhpStorm. * User: daisc * Date: 2018/7/23 * Time: 14:45 */ class Lock { private static $_instance ; private $_redis ; private function __construct() { $this ->_redis = new Redis(); $this ->_redis ->connect( '127.0.0.1' ); } public static function getInstance() { if (self:: $_instance instanceof self) { return self:: $_instance ; } return self:: $_instance = new self(); } /** * @function 加锁 * @param $key 锁名称 * @param $expTime 过期时间 */ public function set( $key , $expTime ) { //初步加锁 $isLock = $this ->_redis->setnx( $key ,time()+ $expTime ); if ( $isLock ) { return true; } else { //加锁失败的情况下。判断锁是否已经存在,如果锁存在切已经过期,那么删除锁。进行重新加锁 $val = $this ->_redis->get( $key ); if ( $val && $val <time()) { $this ->del( $key ); } return $this ->_redis->setnx( $key ,time()+ $expTime ); } } /** * @param $key 解锁 */ public function del( $key ) { $this ->_redis->del( $key ); } } $pdo = new PDO( 'mysql:host=127.0.0.1;dbname=test' , 'root' , 'root' ); $lockObj = Lock::getInstance(); //判断是能加锁成功 if ( $lock = $lockObj ->set( 'storage' ,10)) { $sql = "select `number` from storage where id=1 limit 1" ; $res = $pdo ->query( $sql )->fetch(); $number = $res [ 'number' ]; if ( $number >0) { $sql = "insert into `order` VALUES (null,$number)" ; $order_id = $pdo ->query( $sql ); if ( $order_id ) { $sql = "update storage set `number`=`number`-1 WHERE id=1" ; $pdo ->query( $sql ); } } //解锁 $lockObj ->del( 'storage' ); } else { //加锁不成功执行其他操作。 } |
再次进行ab测试,查看测试结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
mysql> select * from ` order `; + ----+--------+ | id | number | + ----+--------+ | 1 | 10 | | 2 | 9 | | 3 | 8 | | 4 | 7 | | 5 | 6 | | 6 | 5 | | 7 | 4 | | 8 | 3 | | 9 | 2 | | 10 | 1 | + ----+--------+ 10 rows in set (0.00 sec) |
发现订单表没有操作同一个库存数据的情况。所以利用redis锁是可以有效的处理高并发的。
这里在加锁的时候其实是可以不需要判断过期时间的,这里我们为了避免造成死锁,所以加一个过期时间的判断。当过期的时候主动删除该锁。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:https://segmentfault.com/a/1190000016251978