热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

生成唯一字符串

场景生成唯一的准考证号,为日期+六位字符(大写字母+数字),如170808ABC123,并希望生成的准考证号是不连续的问题这里简化为返回一个唯一的数字,之前的解决方案是:第一次请求

场景

生成唯一的准考证号,为日期+六位字符(大写字母+数字),如170808ABC123,并希望生成的准考证号是不连续的

问题

这里简化为返回一个唯一的数字,之前的解决方案是:

  1. 第一次请求,返回一个固定的开始数字 + rand(1, 30),并将数字存入memcache
  2. 之后的请求,每次先用memcached->get()读取memcache中上一次的数字,然后加上rand(1, 30),作为本次生成的数字返回,并更新memcache

#伪代码#
$cOnn= new Memcached();
$conn->addServer("localhost", 11211);
$key = '_lock_test_long_';
$num = $conn->get($key);
if($conn->getResultCode() == Memcached::RES_NOTFOUND){
$num = NUM_START;
}
$num += rand(1, 30);
$conn->set($key, $num);
return $num;

这个做法的问题是,并发场景下,两个并发请求从 memcached->get() 中获取了同一个数,然后rand(1,30)也碰巧相同了,则此时两个请求返回了相同的数字,造成生成准考证号重复。

解决

至少有三种解决方法:
A. 使用Memcached::cas方法,get()的同时获取key的cas_token,然后累加随机数,最后更新memcache时使用cas(),如果更新失败,说明这个key已经在当前请求的get()后发生了更改,生成失败。

#伪代码#
$cOnn= new Memcached();
$conn->addServer('localhost', 11211);
$key = '_lock_test_long_';
do {
$num = $conn->get($key, null, $cas);
/* 如果key不存在, 创建并进行一个原子添加*/
if ($conn->getResultCode() == Memcached::RES_NOTFOUND) {
$num = NUM_START;
$conn->add($key, $num);
} else {
$num += rand(1, 30);
$conn->cas($cas, $key, $num);
}
} while ($conn->getResultCode() != Memcached::RES_SUCCESS);
return $num;

B. 使用Memcached::increment方法,return increment($key, rand(1, 30)),用increment函数的原子性保证并发请求得到顺序处理

#伪代码#
$cOnn= new Memcached();
$conn->addServer("localhost", 11211);
$key = '_lock_test_long_';
$num = $conn->increment($key, rand(1, 30), NUM_START);
return $num;

C. 使用Redis::incr,原理同 2,并且没有key存在时,值从0开始。

最后,方法1的缺点是需要额外处理失败的情况,方法2的缺点是CI对memcached的组件封装没有increment方法(汗。。。),需要手工改框架。最后选了方法3。


推荐阅读
author-avatar
手机用户2602915205
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有