Hướng dẫn generate uuid javascript - tạo javascript uuid

Nội dung bài viết

Video học lập trình mỗi ngày

UUID là gì? Hẳn là mỗi developer cũng đã nghe về UUID rồi, nói sơ qua thì UUID là viết tắt của Universally Unique IDentifier, có thể hiểu đó là một định danh duy nhất và không có cái thứ hai. Hay nói cách khác đó là một id duy nhất trong global. Đây cũng là một bài viết trong Series - Mẹo viết javascript, ở đó có nhiều tips hữu ích có thể bạn sẽ cần.

Theo kiablog.com - UUID là viết tắt của Universally Unique IDentifier, hiểu nôm na là một định danh duy nhất trong toàn thể vũ trụ (universal cơ mà) =)). Mục đích của UUID sinh ra là bởi vì:

  • Dữ liệu lớn, kiểu khóa chính auto imcrement cần nhiều byte để lưu hơn. Và khóa chính kiểu này không phù hợp khi mà hệ thống có nhiều server, nhiều client cùng lúc truy cập trên toàn thế giới.
  • Nếu dùng khóa chính kiểu auto imcrement, có thể dễ dàng truy ra được trong database có bao nhiêu record. Thường thấy ở đường dẫn kiểu "domain.com/user/12345".

Bởi vậy UUID ra đời nhằm khắc phục những yếu điểm trên. Vậy nếu bạn đủ sức xây dựng một hệ thống với nhiều server, phục vụ hàng tỉ tỉ user hoặc chỉ đơn giản là không muốn để lộ id ra ngoài, hãy nghĩ tới UUID. Với mục đích của bài đăng này, tôi sẽ sử dụng javascript để tạo một UUID. Chứ không phải dành thời gian để phân tích về UUID.

Tạo UUID trong javascript

Gần đây tôi đã suy nghĩ rất nhiều về UUID. Hệ thống mà tôi được thừa hưởng tại nơi làm việc bị ảnh hưởng bởi việc sử dụng chúng. Đối với nhiều nhà phát triển, UUID dường như là một cách hoàn toàn tuyệt vời để thiết lập danh tính của một bản ghi trong một hệ thống. Ý tôi là, thật tuyệt vời khi bạn có thể tạo một ID duy nhất cho mọi hệ thống trên thế giới? Có nhiều thư viện đê thực hiện điều đó nhưng một vài ý tưởng tôi đã tìm được tren reddit và một kết quả làm tôi mỹ mãn, mặc dù vẫn có nhiều tranh cãi khác nhau về cách làm này.

// Author: Abhishek Dutta, 12 June 2020
// License: CC0 (https://creativecommons.org/choose/zero/)
function uuid() {
  var temp_url = URL.createObjectURL(new Blob());
  var uuid = temp_url.toString();
  URL.revokeObjectURL(temp_url);
  return uuid.substr(uuid.lastIndexOf('/') + 1); // remove prefix (e.g. blob:null/, blob:www.test.com/, ...)
}

# Cách sử dụng tạo uuid

for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd

Link tham khảo tại abhishekdutta.com

Câu trả lời của Broofa khá trơn tru, thực sự - thông minh ấn tượng, thực sự ... tuân thủ RFC4122, hơi dễ đọc và nhỏ gọn. Đáng kinh ngạc!

Nhưng nếu bạn đang nhìn vào biểu thức thông thường đó, nhiều cuộc gọi lại replace() đó, các cuộc gọi chức năng của ____ 10 và

for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
1 (trong đó anh ta chỉ sử dụng bốn bit của kết quả và lãng phí phần còn lại), bạn có thể bắt đầu tự hỏi về hiệu suất. Thật vậy, Joelpt thậm chí đã quyết định tung ra một RFC cho tốc độ GUID chung với
for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
2.

Nhưng, chúng ta có thể có được sự tuân thủ tốc độ và RFC không? Tôi nói "có! Chúng ta có thể duy trì khả năng đọc không? Chà ... không thực sự, nhưng thật dễ dàng nếu bạn theo dõi. Can we maintain readability? Well... Not really, but it's easy if you follow along.

Nhưng trước tiên, kết quả của tôi, so với Broofa,

for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
3 (câu trả lời được chấp nhận) và
for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
4 không tuân thủ RFC:

                  Desktop   Android
           broofa: 1617ms   12869ms
               e1:  636ms    5778ms
               e2:  606ms    4754ms
               e3:  364ms    3003ms
               e4:  329ms    2015ms
               e5:  147ms    1156ms
               e6:  146ms    1035ms
               e7:  105ms     726ms
             guid:  962ms   10762ms
generateQuickGuid:  292ms    2961ms
  - Note: 500k iterations, results will vary by browser/CPU.

Vì vậy, bằng lần lặp tối ưu hóa thứ 6 của tôi, tôi đã đánh bại câu trả lời phổ biến nhất hơn 12 lần, câu trả lời được chấp nhận hơn 9 lần và câu trả lời không tuân thủ nhanh 2-3 lần. Và tôi vẫn là RFC & NBSP; 4122 tuân thủ.12 times, the accepted answer by over 9 times, and the fast-non-compliant answer by 2-3 times. And I'm still RFC 4122 compliant.

Quan tâm đến làm thế nào? Tôi đã đặt nguồn đầy đủ trên http://jsfiddle.net/jcward/7hyac/3/ và trên https://jsben.ch/xczxs

Để giải thích, hãy bắt đầu với mã của Broofa:

function broofa() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
        return v.toString(16);
    });
}

console.log(broofa())

Vì vậy, nó thay thế

for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
5 bằng bất kỳ chữ số thập lục phân ngẫu nhiên nào,
for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
6 bằng dữ liệu ngẫu nhiên (ngoại trừ việc bỏ hai bit hàng đầu lên
for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
7 trên mỗi thông số RFC) và Regex không khớp với các ký tự
for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
8 hoặc
for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
9, vì vậy anh ta không phải giao dịch với họ. Rất, rất trơn.

Điều đầu tiên cần biết là các cuộc gọi chức năng rất tốn kém, cũng như các biểu thức thông thường (mặc dù anh ta chỉ sử dụng 1, nó có 32 cuộc gọi lại, một cho mỗi trận đấu và trong mỗi 32 cuộc gọi lại mà nó gọi là math.random () và v. ToString (16)).

Bước đầu tiên hướng tới hiệu suất là loại bỏ các chức năng gọi lại và các chức năng gọi lại của nó và sử dụng một vòng lặp đơn giản. Điều này có nghĩa là chúng ta phải đối phó với các ký tự

for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
8 và
for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
9 trong khi Broofa thì không. Ngoài ra, lưu ý rằng chúng ta có thể sử dụng lập chỉ mục mảng chuỗi để giữ kiến ​​trúc mẫu chuỗi trơn của mình:

function e1() {
    var u='',i=0;
    while(i++<36) {
        var c='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'[i-1],r=Math.random()*16|0,v=c=='x'?r:(r&0x3|0x8);
        u+=(c=='-'||c=='4')?c:v.toString(16)
    }
    return u;
}

console.log(e1())

Về cơ bản, logic bên trong tương tự, ngoại trừ chúng tôi kiểm tra

for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
8 hoặc
for(var i=0; i<10; ++i) { console.log(uuid()); }

f6ca05c0-fad5-46fc-a237-a8e930e7cb49
6a88664e-51e1-48c3-a85e-7bf00467e9e6
e6050f4c-e86d-4081-9376-099bfbef2c30
bde3da3c-b318-4498-8a03-9a773afa84bd
ba0fda03-f806-4c2f-b6f5-1e74a299e603
62b2edc3-b09f-4bf9-8dbf-c4d599479a29
e70c0609-22ad-4493-abcc-0e3445291397
920255b2-1838-497d-bc33-56550842b378
45559c64-971c-4236-9cfc-706048b60e70
4bc4bbb9-1e90-432b-99e8-277b40af92cd
9 và sử dụng vòng lặp trong thời gian (thay vì replace() gọi lại) giúp chúng tôi cải thiện gần 3 lần!

Bước tiếp theo là một bước nhỏ trên máy tính để bàn nhưng tạo ra sự khác biệt tốt trên thiết bị di động. Hãy thực hiện ít các cuộc gọi toán học hơn () và sử dụng tất cả các bit ngẫu nhiên đó thay vì ném 87% trong số chúng bằng một bộ đệm ngẫu nhiên được chuyển ra mỗi lần lặp. Cũng hãy di chuyển định nghĩa mẫu đó ra khỏi vòng lặp, chỉ trong trường hợp nó giúp:

function e2() {
    var u='',m='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx',i=0,rb=Math.random()*0xffffffff|0;
    while(i++<36) {
        var c=m[i-1],r=rb&0xf,v=c=='x'?r:(r&0x3|0x8);
        u+=(c=='-'||c=='4')?c:v.toString(16);rb=i%8==0?Math.random()*0xffffffff|0:rb>>4
    }
    return u
}

console.log(e2())

Điều này giúp chúng tôi tiết kiệm 10-30% tùy thuộc vào nền tảng. Không tệ. Nhưng bước lớn tiếp theo sẽ loại bỏ chức năng ToString hoàn toàn với một tác phẩm kinh điển tối ưu hóa - bảng tra cứu. Một bảng tra cứu 16 phần tử đơn giản sẽ thực hiện công việc của ToString (16) trong thời gian ngắn hơn nhiều:

function e3() {
    var h='0123456789abcdef';
    var k='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
    /* same as e4() below */
}
function e4() {
    var h=['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'];
    var k=['x','x','x','x','x','x','x','x','-','x','x','x','x','-','4','x','x','x','-','y','x','x','x','-','x','x','x','x','x','x','x','x','x','x','x','x'];
    var u='',i=0,rb=Math.random()*0xffffffff|0;
    while(i++<36) {
        var c=k[i-1],r=rb&0xf,v=c=='x'?r:(r&0x3|0x8);
        u+=(c=='-'||c=='4')?c:h[v];rb=i%8==0?Math.random()*0xffffffff|0:rb>>4
    }
    return u
}

console.log(e4())

Tối ưu hóa tiếp theo là một cổ điển khác. Vì chúng tôi chỉ xử lý bốn bit đầu ra trong mỗi lần lặp vòng lặp, hãy cắt số vòng lặp làm đôi và xử lý tám bit trong mỗi lần lặp. Điều này là khó khăn vì chúng tôi vẫn phải xử lý các vị trí bit tuân thủ RFC, nhưng nó không quá khó. Sau đó, chúng tôi phải thực hiện một bảng tra cứu lớn hơn (16x16 hoặc 256) để lưu trữ 0x00 - 0xff và chúng tôi chỉ xây dựng nó một lần, bên ngoài hàm e5 ().

var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e5() {
    var k=['x','x','x','x','-','x','x','-','4','x','-','y','x','-','x','x','x','x','x','x'];
    var u='',i=0,rb=Math.random()*0xffffffff|0;
    while(i++<20) {
        var c=k[i-1],r=rb&0xff,v=c=='x'?r:(c=='y'?(r&0x3f|0x80):(r&0xf|0x40));
        u+=(c=='-')?c:lut[v];rb=i%4==0?Math.random()*0xffffffff|0:rb>>8
    }
    return u
}

console.log(e5())

Tôi đã thử một E6 () xử lý 16 bit tại một thời điểm, vẫn sử dụng LUT 256 phần tử và nó cho thấy lợi nhuận tối ưu hóa giảm dần. Mặc dù nó có ít lần lặp hơn, logic bên trong rất phức tạp do quá trình xử lý tăng lên và nó thực hiện tương tự trên máy tính để bàn và chỉ nhanh hơn ~ 10% trên thiết bị di động.

Kỹ thuật tối ưu hóa cuối cùng để áp dụng - bỏ vòng lặp. Vì chúng tôi đang lặp lại một số lần cố định, về mặt kỹ thuật, chúng tôi có thể viết tất cả bằng tay này. Tôi đã thử điều này một lần với một biến ngẫu nhiên duy nhất,

                  Desktop   Android
           broofa: 1617ms   12869ms
               e1:  636ms    5778ms
               e2:  606ms    4754ms
               e3:  364ms    3003ms
               e4:  329ms    2015ms
               e5:  147ms    1156ms
               e6:  146ms    1035ms
               e7:  105ms     726ms
             guid:  962ms   10762ms
generateQuickGuid:  292ms    2961ms
  - Note: 500k iterations, results will vary by browser/CPU.
5, mà tôi tiếp tục chỉ định lại và hiệu suất được giảm bớt. Nhưng với bốn biến được gán dữ liệu ngẫu nhiên lên phía trước, sau đó sử dụng bảng tra cứu và áp dụng các bit RFC thích hợp, phiên bản này hút tất cả:

var lut = []; for (var i=0; i<256; i++) { lut[i] = (i<16?'0':'')+(i).toString(16); }
function e7()
{
    var d0 = Math.random()*0xffffffff|0;
    var d1 = Math.random()*0xffffffff|0;
    var d2 = Math.random()*0xffffffff|0;
    var d3 = Math.random()*0xffffffff|0;
    return lut[d0&0xff]+lut[d0>>8&0xff]+lut[d0>>16&0xff]+lut[d0>>24&0xff]+'-'+
    lut[d1&0xff]+lut[d1>>8&0xff]+'-'+lut[d1>>16&0x0f|0x40]+lut[d1>>24&0xff]+'-'+
    lut[d2&0x3f|0x80]+lut[d2>>8&0xff]+'-'+lut[d2>>16&0xff]+lut[d2>>24&0xff]+
    lut[d3&0xff]+lut[d3>>8&0xff]+lut[d3>>16&0xff]+lut[d3>>24&0xff];
}

console.log(e7())

Sửa đổi: http://jcward.com/uuid.js -

                  Desktop   Android
           broofa: 1617ms   12869ms
               e1:  636ms    5778ms
               e2:  606ms    4754ms
               e3:  364ms    3003ms
               e4:  329ms    2015ms
               e5:  147ms    1156ms
               e6:  146ms    1035ms
               e7:  105ms     726ms
             guid:  962ms   10762ms
generateQuickGuid:  292ms    2961ms
  - Note: 500k iterations, results will vary by browser/CPU.
6

Điều thú vị là, tạo ra 16 byte dữ liệu ngẫu nhiên là phần dễ dàng. Toàn bộ thủ thuật đang thể hiện nó ở định dạng chuỗi với sự tuân thủ của RFC và nó được thực hiện chặt chẽ nhất với 16 byte dữ liệu ngẫu nhiên, một vòng lặp và bảng tra cứu không được kiểm soát.

Tôi hy vọng logic của tôi là chính xác - thật dễ dàng để phạm sai lầm trong loại công việc tẻ nhạt này. Nhưng đầu ra có vẻ tốt với tôi. Tôi hy vọng bạn thích chuyến đi điên rồ này thông qua tối ưu hóa mã!

Được thông báo: Mục tiêu chính của tôi là thể hiện và dạy các chiến lược tối ưu hóa tiềm năng. Các câu trả lời khác bao gồm các chủ đề quan trọng như va chạm và số thực sự ngẫu nhiên, rất quan trọng để tạo UUID tốt. my primary goal was to show and teach potential optimization strategies. Other answers cover important topics such as collisions and truly random numbers, which are important for generating good UUIDs.