如何创建一个适合PostgreSQL中会话ID的随机字符串?

时间:2010-10-19 17:05:03

标签: postgresql random

我想使用PostgreSQL创建一个随机字符串用于会话验证。我知道我可以使用SELECT random()获得一个随机数字,所以我尝试SELECT md5(random()),但这不起作用。我怎么能这样做?

13 个答案:

答案 0 :(得分:205)

您可以像这样修复您的初始尝试:

SELECT md5(random()::text);

比其他一些建议简单得多。 : - )

答案 1 :(得分:66)

我建议这个简单的解决方案:

这是一个非常简单的函数,它返回给定长度的随机字符串:

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)

答案 2 :(得分:28)

在Marcin的解决方案的基础上,您可以使用任意字母表(在这种情况下,所有62个ASCII字母数字字符):

SELECT array_to_string(array 
       ( 
              select substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', trunc(random() * 62)::integer + 1, 1)
              FROM   generate_series(1, 12)), '');

答案 3 :(得分:13)

我最近在使用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方法的第二个参数决定了字符串的长度。

答案 4 :(得分:13)

您可以从UUID获得128位随机数。这是在现代PostgreSQL中完成工作的方法。

CREATE EXTENSION pgcrypto;
SELECT gen_random_uuid();

           gen_random_uuid            
--------------------------------------
 202ed325-b8b1-477f-8494-02475973a28f

可能是worth reading the docs on UUID too

  

数据类型uuid存储由 RFC 4122,ISO / IEC 9834-8:2005 和相关标准定义的通用唯一标识符(UUID)。 (有些系统将此数据类型称为全局唯一标识符,或称为GUID。)此标识符是 128位数量,由选择的算法生成,以使其不太可能相同标识符将由已知Universe中的任何其他人使用相同的算法生成。因此,对于分布式系统,这些标识符提供了比序列生成器更好的唯一性保证,序列生成器在单个数据库中是唯一的。

与UUID的碰撞有多罕见,或者是可猜测的?假设他们是随机的,

  

需要生成大约100万亿版本的4个UUID才能获得单个重复的十亿分之一(&#34;碰撞&#34;)。只有在生成261个UUID(2.3 x 1018或2.3 quintillion)之后,一次碰撞的几率才会上升到50%。将这些数字与数据库相关联,并考虑到版本4 UUID冲突概率是否可忽略的问题,请考虑包含2.3个quintillion版本4 UUID的文件,其中50%的可能性包含一个UUID冲突。它假设没有其他数据或开销,大小为36艾字节,是目前存在的最大数据库的数千倍,大约为千兆字节。以每秒生成10亿UUID的速率,生成文件的UUID需要73年。假设没有备份或冗余,它还需要大约360万个10TB硬盘或磁带盒来存储它。在典型的&#34;磁盘到缓冲区中读取文件&#34;对于单个处理器,每秒1千兆位的传输速率需要超过3000年。由于驱动器的不可恢复读取错误率是每1018位读取1位,充其量,当文件包含大约1020位时,仅从端到端读取文件一次将导致至少大约100倍的错误读取UUID而不是重复。毫无疑问,存储,网络,电源和其他硬件和软件错误比UUID重复问题的频率高出数千倍。 - source wikipedia

总之,

  • UUID已标准化。
  • gen_random_uuid()是以128位(2 ** 128种组合)存储的128位随机数。 0-浪费。
  • random() only generates 52 bits of random in PostgreSQL (2**52 combinations).
  • 存储为UUID的
  • md5()为128位,但它只能与其输入一样随机(52 bits if using random()
  • 存储为文本的
  • md5()是288位,但它只能与其输入(52 bits if using random())一样随机 - 超过UUID大小的两倍和随机性的一小部分)
  • md5()作为哈希,可以如此优化,以至于它不会有效地做很多事情。
  • UUID对存储非常有效:PostgreSQL提供的类型恰好是128位。与textvarchar等不同,它存储为varlena,其中包含字符串长度的开销。
  • PostgreSQL漂亮的UUID附带了一些默认的运算符,演员和功能。

答案 5 :(得分:11)

虽然默认情况下不活动,但您可以激活其中一个核心扩展程序:

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

前导号码是散列标识符。有几种算法可用,每种算法都有自己的标识符:

  • md5:$ 1 $
  • bf:$ 2a $ 06 $
  • des:无标识符
  • xdes:_J9 ..

有关扩展程序的更多信息:


修改

如Evan Carrol所示,自v9.4起,您可以使用gen_random_uuid()

http://www.postgresql.org/docs/9.4/static/pgcrypto.html

答案 6 :(得分:10)

请使用string_agg

SELECT string_agg (substr('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ceil (random() * 62)::integer, 1), '')
FROM   generate_series(1, 45);

我在MD5上使用它也可以生成UUID。我只想要一个比random ()整数更多位的随机值。

答案 7 :(得分:6)

我认为你本身并不是在寻找随机字符串。会话验证所需的是一个保证唯一的字符串。您是否存储会话验证信息以供审核?在这种情况下,您需要字符串在会话之间是唯一的。我知道两种相当简单的方法:

  1. 使用序列。适合在单个数据库上使用。
  2. 使用UUID。在分布式环境中也非常独特,非常好。
  3. UUID 保证凭借其生成算法是唯一的;实际上 非常 不太可能在任何时候在任何机器上生成两个相同的数字(请注意,这比随机字符串强得多,后者有一个周期比UUID小得多。)

    您需要加载uuid-ossp扩展名才能使用UUID。安装完成后,在SELECT,INSERT或UPDATE调用中调用任何可用的uuid_generate_vXXX()函数。 uuid类型是一个16字节的数字,但它也有一个字符串表示。

答案 8 :(得分:4)

select * from md5(to_char(random(), '0.9999999999999999'));

答案 9 :(得分:4)

INTEGER参数定义字符串的长度。保证以相同的概率覆盖所有62个字母字符(与在互联网上浮动的其他一些解决方案不同)。

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;

答案 10 :(得分:4)

@Kavius建议使用pgcrypto,而不是gen_saltgen_random_bytes呢?那么sha512而不是md5呢?

create extension if not exists pgcrypto;
select digest(gen_random_bytes(1024), 'sha512');

文档:

  

F.25.5。随机数据函数

     

gen_random_bytes(count integer)返回bytea

     

返回计数加密强随机字节。最多1024   可以一次提取字节。这是为了避免耗尽   随机生成器池。

答案 11 :(得分:2)

create extension if not exists pgcrypto;

然后

SELECT encode(gen_random_bytes(20),'base64')

甚至

SELECT encode(gen_random_bytes(20),'hex')

这是 20 字节 = 160 位的随机性(例如,只要 sha1)。

答案 12 :(得分:1)

func saveImageAtPath(image: NSImage, path: String)
{
    if let data = image.TIFFRepresentation
    {
        let bitmap = NSBitmapImageRep(data: data)
        let pngImage = bitmap?.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])

        NSLog("Path : %@", path)

        if let _ = pngImage?.writeToFile(path, atomically: false)
        {
            NSLog("Everything should work...")
        }
    }
}