最近遇到一個問題:高並發環境下,如何避免MYSQL一張表里的某些列不要重復。
同其他博友一樣 https://blog.csdn.net/jacketinsysu/article/details/51526284 提出這個問題。
方式一:使用Mysql自帶的列唯一索引(Unique)
可以在聲明表的時候就加入這樣一個約束(下面是mysql的語法):
CREATE TABLE IF NOT EXISTS `test_orderno2` ( `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(50) NOT NULL COMMENT '名稱', `order_no` bigint unsigned NOT NULL COMMENT '編號', `createtime` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '創建時間', PRIMARY KEY (`id`), UNIQUE KEY `uniq_order_no` (`order_no`), UNIQUE KEY `uniq_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT '測試編號';
CREATE TABLE `test_orderno3` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `order_no` bigint(20) unsigned NOT NULL COMMENT '自定義編號', `order_simple` char(21) NOT NULL COMMENT '簡單編號', `order_complex` char(30) NOT NULL COMMENT '復雜編號', `createtime` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '創建時間', PRIMARY KEY (`id`), UNIQUE KEY `uniq_order_no` (`order_no`), UNIQUE KEY `uniq_order_simple` (`order_simple`), UNIQUE KEY `uniq_order_complex` (`order_complex`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='測試編號3';
這里對 test_orderno2 表設置了兩個列的唯一索引,在插入數據的同時會檢查該表這兩列是存在,存在則會拋出sql錯誤
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '2018000001' for key 'uniq_order_no
新增行的PHP代碼如下:
$count = $test_orderno2->count(); $addData['name'] = implode( '',explode('.',uniqid('',true)) ); $addData['order_no'] = sprintf( "2018%06s",++$count ); $addData['createtime'] = time(); $status = $test_orderno2->add($addData);
$test_orderno3 = M('test_orderno3'); $count = $test_orderno3->count(); $addData['order_no'] = sprintf( "2018%06s",++$count ); // 唯一性較高的唯一編號(21位) $system_no = uniqid(); $system_no_date = date( 'YmdHis',hexdec(substr($system_no,0,8)) ); $system_no_rand = sprintf( "%07s",hexdec(substr($system_no,8,5)) ); $addData['order_simple'] = $system_no_date.$system_no_rand; // 唯一性高的唯一編號(30位) $system_no = implode( '',explode('.',uniqid('',true)) ); $system_no_date = date( 'YmdHis',hexdec(substr($system_no,0,8)) ); $system_no_rand = sprintf( "%07s",hexdec(substr($system_no,8,5)) ); $more_entropy = substr($system_no,13,9); $addData['order_complex'] = $system_no_date.$system_no_rand.$more_entropy; $addData['createtime'] = time(); // $this->ajaxReturn($addData); $status = $test_orderno3->add($addData); $this->ajaxReturn(array('status'=>$status));
這樣在高並發的時候只滿足一個客戶端請求,其他客戶端請求全部會拋出sql錯誤。
如果業務需求在高並發的時候要正常處理多個客戶端請求成功,則不建議采用該方式
方式二:使用PHP代碼進行唯一編號的分配
string uniqid ([ string $prefix = "" [, bool $more_entropy = false ]] )
PHP函數 uniqid 生成唯一編號代碼如下:
/** * 獲取一個帶前綴、基於當前時間微秒數的唯一ID。 */ // 最簡單的一種 $uniqid = uniqid(); // => string '5b11088f3c40a' (length=13) // 添加前綴 $uniqid = uniqid('j_'); // => string 'j_5b11088f3c40a' (length=15) // 設置combined linear congruential generator,使得唯一性更強 $uniqid = uniqid('',true); // => string '5b11094264cd58.46100058' (length=23)
了解到 uniqid 的使用,則可以根據該函數來做唯一列
// 一行代碼得到唯一列 $uniqid = implode( '',explode('.',uniqid('',true)) ); // => string '5b1109c17228c598550406' (length=22)
方式三:使用PHP代碼進行唯一編號的分配,並分配是數字日期型
直接上代碼:
1 // 1. 並發量高使用 22 (唯一性高) 2 $system_no = implode( '',explode('.',uniqid('',true)) ); 3 var_dump( $system_no ); 4 $system_no_date = date( 'YmdHis',hexdec(substr($system_no,0,8)) ); 5 $system_no_rand = sprintf( "%07s",hexdec(substr($system_no,8,5)) ); 6 $more_entropy = substr($system_no,13,9); 7 // var_dump( $system_no_date."-".$system_no_rand."-".$more_entropy ); 8 var_dump( $system_no_date.$system_no_rand.$more_entropy );
line.2-------------得到方式二中的22位唯一字符串
line.4-------------將22中前8位取出,經過研究發現前8既然是當前時間戳16進制,所以根據公式可以得出當前時間戳的YmdHis格式
line.5-------------隨后又出去8位到13位,經過研究發現這5位也是16進制,想轉化成數字需要鎖定FFFFF的值=1048575,最大7位,則使用%07s不足7位則前面補0保持7位
line.6-------------最后還剩下9位數字是為了增加唯一性的,則不用理會
line.8-------------30位的日期型唯一編號便出爐了
1 // 2. 通常返回 13 (唯一性較高) 2 $system_no = uniqid(); 3 var_dump( $system_no ); 4 $system_no_date = date( 'YmdHis',hexdec(substr($system_no,0,8)) ); 5 $system_no_rand = sprintf( "%07s",hexdec(substr($system_no,8,5)) ); 6 // var_dump( $system_no_date."-".$system_no_rand ); 7 var_dump( $system_no_date.$system_no_rand );
line.2-------------得到方式二中的13位唯一字符串
line.4-------------將22中前8位取出,經過研究發現前8既然是當前時間戳16進制,所以根據公式可以得出當前時間戳的YmdHis格式
line.5-------------隨后又出去8位到13位,經過研究發現這5位也是16進制,想轉化成數字需要鎖定FFFFF的值=1048575,最大7位,則使用%07s不足7位則前面補0保持7位
line.7-------------21位的日期型唯一編號便出爐了
