我想制作一个随机字符串,以用于使用PostgreSQL进行会话验证。我知道我可以使用来获得一个随机数SELECT random()
,所以我尝试了SELECT md5(random())
,但这是行不通的。我怎样才能做到这一点?
random()
ness的情况)。如果不是我所假设的,那么我的答案需要迎合经过提炼的问题。
我想制作一个随机字符串,以用于使用PostgreSQL进行会话验证。我知道我可以使用来获得一个随机数SELECT random()
,所以我尝试了SELECT md5(random())
,但这是行不通的。我怎样才能做到这一点?
random()
ness的情况)。如果不是我所假设的,那么我的答案需要迎合经过提炼的问题。
Answers:
我建议这个简单的解决方案:
这是一个非常简单的函数,它返回给定长度的随机字符串:
Create or replace function random_string(length integer) returns text as
$$
declare
chars text[] := '{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z}';
result text := '';
i integer := 0;
begin
if length < 0 then
raise exception 'Given length cannot be less than 0';
end if;
for i in 1..length loop
result := result || chars[1+random()*(array_length(chars, 1)-1)];
end loop;
return result;
end;
$$ language plpgsql;
用法:
select random_string(15);
输出示例:
select random_string(15) from generate_series(1,15);
random_string
-----------------
5emZKMYUB9C2vT6
3i4JfnKraWduR0J
R5xEfIZEllNynJR
tMAxfql0iMWMIxM
aPSYd7pDLcyibl2
3fPDd54P5llb84Z
VeywDb53oQfn9GZ
BJGaXtfaIkN4NV8
w1mvxzX33NTiBby
knI1Opt4QDonHCJ
P9KC5IBcLE0owBQ
vvEEwc4qfV4VJLg
ckpwwuG8YbMYQJi
rFf6TchXTO3XsLs
axdQvaLBitm6SDP
(15 rows)
chars[1+random()*(array_length(chars, 1)-1)]
用chars[ceil(61 * random())]
ORDER BY random()
。哪个更快?
您可以像这样修复您的初始尝试:
SELECT md5(random()::text);
比其他一些建议简单得多。:-)
SELECT concat(md5(random()::text), md5(random()::text));
如果要在中间某个位置(例如50个字符),则可以使用其中的一个子字符串: SELECT substr(concat(md5(random()::text), md5(random()::text)), 0, 50);
gen_random_uuid()
以下方法检查完全不同的方法:更快,更随机,更有效地存储在数据库中。
基于Marcin的解决方案,您可以执行以下操作以使用任意字母(在这种情况下,所有62个ASCII字母数字字符):
SELECT array_to_string(array
(
select substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', trunc(random() * 62)::integer + 1, 1)
FROM generate_series(1, 12)), '');
Check out this for a totally different method using gen_random_uuid()
:更快,更随机,更有效地存储在数据库中。
您可以从UUID获得128位随机数。这是在现代PostgreSQL中完成工作的方法。
CREATE EXTENSION pgcrypto;
SELECT gen_random_uuid();
gen_random_uuid
--------------------------------------
202ed325-b8b1-477f-8494-02475973a28f
uuid数据类型存储RFC 4122,ISO / IEC 9834-8:2005和相关标准定义的通用唯一标识符(UUID)。(某些系统将这种数据类型称为全局唯一标识符,或称为GUID。)此标识符是128位的数量,该数量是由一种算法生成的,该算法的选择使其他任何人都不太可能生成同一标识符在已知的宇宙中使用相同的算法。因此,对于分布式系统,这些标识符提供了比仅在单个数据库中唯一的序列生成器更好的唯一性保证。
与UUID发生碰撞的可能性有多少?假设它们是随机的
将需要生成大约100万亿个版本4 UUID,以使其有十亿分之一的可能性发生一次重复(“冲突”)。仅在生成261个UUID(2.3 x 10 ^ 18或2.3十亿个)之后,一次碰撞的机会就会增加到50%。将这些数字与数据库相关联,并考虑到版本4 UUID冲突的可能性是否可以忽略不计的问题,请考虑包含2.3亿个版本4 UUID的文件,其中有50%的机会包含一次UUID冲突。假设没有其他数据或开销,那么它的大小将为36 EB,比目前存在的最大数据库(PB级)大数千倍。以每秒生成10亿个UUID的速度,将花费73年的时间来生成文件的UUID。它还需要大约3。假设没有备份或冗余,则可以存储600万个10 TB的硬盘驱动器或盒式磁带。以每秒1 GB的典型“磁盘到缓冲区”传输速率读取文件,单个处理器将需要3000年以上的时间。由于驱动器不可恢复的读取错误率是每读取1018位最多1位,而文件将包含约1020位,因此仅从头到尾读取一次文件至少会导致大约100倍以上的错误-读取UUID而不是重复读取。与UUID复制问题相比,存储,网络,电源以及其他硬件和软件错误的发生率无疑是数千倍。一个处理器每秒1吉比特的传输速率将需要3000多年。由于驱动器不可恢复的读取错误率是每读取1018位最多1位,而文件将包含约1020位,因此仅从头到尾读取一次文件至少会导致大约100倍以上的错误-读取UUID而不是重复读取。与UUID复制问题相比,存储,网络,电源以及其他硬件和软件错误的发生率无疑是数千倍。一个处理器每秒1吉比特的传输速率将需要3000多年。由于驱动器不可恢复的读取错误率是每读取1018位最多1位,而文件将包含约1020位,因此仅从头到尾读取一次文件至少会导致大约100倍以上的错误-读取UUID而不是重复读取。无疑,存储,网络,电源以及其他硬件和软件错误的发生频率将比UUID复制问题高数千倍。
资料来源:维基百科
综上所述,
gen_random_uuid()
是128位随机数,以128位(2 ** 128个组合)存储。零浪费。random()
在PostgreSQL中仅生成52位随机数(2 ** 52种组合)。md5()
作为UUID存储的是128位,但只能是其输入的随机数(如果使用,则为52位random()
)md5()
以文本形式存储的是288位,但只能是其输入的随机数(如果使用,则为52位random()
)-超过UUID大小的两倍和随机性的一小部分)md5()
作为一个哈希,可以进行优化以至于它不能有效地执行很多操作。text
和不同varchar
,等存储为varlena
,对于字符串的长度有开销。我最近在玩PostgreSQL,我想我找到了一个更好的解决方案,只使用内置PostgreSQL方法-没有pl / pgsql。唯一的限制是它当前仅生成UPCASE字符串,数字或小写字符串。
template1=> SELECT array_to_string(ARRAY(SELECT chr((65 + round(random() * 25)) :: integer) FROM generate_series(1,12)), '');
array_to_string
-----------------
TFBEGODDVTDM
template1=> SELECT array_to_string(ARRAY(SELECT chr((48 + round(random() * 9)) :: integer) FROM generate_series(1,12)), '');
array_to_string
-----------------
868778103681
该generate_series
方法的第二个参数决定了字符串的长度。
array_to_string(ARRAY(SELECT chr((65 + round((random()+my_id-my) * 25)) :: integer) FROM generate_series(1,8)), '')
array_to_string(ARRAY(SELECT chr((65 + round((random() * 25 + id) :: integer % 25 )) :: integer) FROM generate_series(1, 60)), '');
虽然默认情况下未激活,但是您可以激活以下核心扩展之一:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
然后,您的语句成为对gen_salt()的简单调用,它会生成一个随机字符串:
select gen_salt('md5') from generate_series(1,4);
gen_salt
-----------
$1$M.QRlF4U
$1$cv7bNJDM
$1$av34779p
$1$ZQkrCXHD
前导数字是哈希标识符。有几种算法,每种算法都有自己的标识符:
有关扩展的更多信息:
编辑
如Evan Carrol所述,从v9.4开始,您可以使用 gen_random_uuid()
$1$
?那是一个哈希类型标识符(md5 == 1),其余是随机值。
我认为您本身并不是在寻找随机字符串。会话验证所需的是一个保证唯一的字符串。您是否存储会话验证信息以进行审核?在这种情况下,您需要在会话之间使字符串唯一。我知道两种非常简单的方法:
UUID 的生成算法可确保其唯一性;有效是非常不可能的,你会不会产生任何机器上有两个相同的数字,在任何时候,曾(注意,这是不是随机字符串,它比的UUID小得多的周期性更强)。
您需要加载uuid-ossp扩展名才能使用UUID。安装后,在SELECT,INSERT或UPDATE调用中调用任何可用的uuid_generate_vXXX()函数。uuid类型是一个16字节的数字,但它也具有字符串表示形式。
INTEGER参数定义字符串的长度。保证以相等的概率覆盖所有62个字母数字字符(与Internet上其他一些浮动解决方案不同)。
CREATE OR REPLACE FUNCTION random_string(INTEGER)
RETURNS TEXT AS
$BODY$
SELECT array_to_string(
ARRAY (
SELECT substring(
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
FROM (ceil(random()*62))::int FOR 1
)
FROM generate_series(1, $1)
),
''
)
$BODY$
LANGUAGE sql VOLATILE;
Check out this for a totally different method using gen_random_uuid()
:更快,更随机,更有效地存储在数据库中。
gen_random_uuid()
据我所知,它是在9.4版中发布的,该版本于2014-12-18发布,比您的答案低了一年多。额外的nitpick:答案只有3 1/2岁了:-)但是,现在我们有了gen_random_uuid()
,这是您应该使用的。因此,我会支持您的回答。
@Kavius建议使用pgcrypto
,而不是使用gen_salt
什么gen_random_bytes
呢?而sha512
不是md5
呢?
create extension if not exists pgcrypto;
select digest(gen_random_bytes(1024), 'sha512');
文件:
F.25.5。随机数据功能
gen_random_bytes(计数整数)返回bytea
返回计数加密强度高的随机字节。一次最多可以提取1024个字节。这是为了避免耗尽随机性生成器池。