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