Learn And Life.

redis并发性与设计

也许以前对并发性理解的不够深,也许对一些概念和设计的理解还不够好,这样往往会在写代码的过程中,会留下一个感觉不是bug的bug,让你狠抓头皮,不能想明白,到底发生了什么?所以在考虑问题和在设计过程中,需要注意一些比较成熟的理念,认识到你使用的产品和所写的业务是一种什么的业务,并发性和耗时在一定程度上有一定的关联,你可能想想不到这种关联在什么程度上影响着你的代码的执行,先看下以下的代码,抛开设计,只看代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$redis = new redis();
$redis->connect('127.0.0.1',6379);
$key = 'users';
$page = $argv[1];
$offset = 10;
$key = 'users';
$result = $redis->zrange($key,($page-1) * $offset - 1, $offset);
if(empty($result)){
$redis->del($key);
$redis->expire($key,30);
$i = 0;
while($i < 100){
$redis->zIncrBy($key,1,$i);
sleep(2);
$i++;
}
}

代码很简单,就是如果缓存为空,就重新写缓存,也许你并没有发现这段代码到底存在什么问题,那么就看看下面的问题,先执行:

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> zrange users 0 -1 withscores
1) "0"
2) "1"
3) "1"
4) "1"
5) "2"
6) "1"
7) "3"
8) "1"
9) "4"
10) "1"

哇,进去了呢,然后再来一次

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> zrange users 0 -1 withscores
1) "0"
2) "1"
3) "1"
4) "1"
5) "2"
6) "1"
7) "3"
8) "1"
9) "4"
10) "1"

哇,还是进去了呢,不经喊了句,太完美了,暗暗自喜!难道真没问题了么?然后开启两个进程,看看发生了什么了吧,请看下面的执行结果

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> zrange users 0 -1 withscores
1) "0"
2) "1"
3) "1"
4) "1"
5) "2"
6) "2"
7) "3"
8) "2"
9) "4"
10) "2"

糟糕,怎么会这样呢?这时候,你可能在问,靠,不会吧?但是事实就是这样,你的程序出现bug了,如果一个在线上运行的代码,你很有可能不知道怎么发生的,然后盯着你的代码在看,这怎么回事呢?没有日志可看,这时候,你傻眼了吧!这就是并发问题,这到底是怎么发生的呢?你删除了key,但是后面一直在运行了,还等不急运行完,然后有一个请求来了,正是这样耗时的操作,导致了你的设计跟你的想法背道而驰,话说回来了,这样的设计是一种比较糟糕的设计,把耗时的操作分离出来才是一种最佳实践,所以,别懒了,读写分离吧,一方面可以加快接口的速度,另一方面,当数据量比较大的时候,你就玩完了,还有就是避免这还种并发性问题,所以,别懒了,做好你的设计吧!想明白自己在做什么!