Truy vấn DynamoDB theo ngày


102

Tôi đến từ nền tảng cơ sở dữ liệu quan hệ và đang cố gắng làm việc với DynamoDB của amazon

Tôi có một bảng có khóa băm "DataID" và phạm vi "CreatedAt" và một loạt các mục trong đó.

Tôi đang cố gắng lấy tất cả các mục được tạo sau một ngày cụ thể và được sắp xếp theo ngày. Điều này khá đơn giản trong cơ sở dữ liệu quan hệ.

Trong DynamoDB, điều gần nhất mà tôi có thể tìm thấy là một truy vấn và sử dụng khóa phạm vi lớn hơn bộ lọc. Vấn đề duy nhất là để thực hiện một truy vấn, tôi cần một khóa băm để đánh bại mục đích.

Vậy tôi đang làm gì sai? Lược đồ bảng của tôi có sai không, khóa băm không phải là duy nhất? hoặc có cách nào khác để truy vấn?

Câu trả lời:


34

Câu trả lời cập nhật:

DynamoDB cho phép đặc tả các chỉ mục phụ để hỗ trợ trong loại truy vấn này. Các chỉ mục phụ có thể là toàn cục, nghĩa là chỉ mục trải dài toàn bộ bảng qua các khóa băm hoặc cục bộ có nghĩa là chỉ mục sẽ tồn tại trong mỗi phân vùng khóa băm, do đó yêu cầu khóa băm cũng phải được chỉ định khi thực hiện truy vấn.

Đối với trường hợp sử dụng trong câu hỏi này, bạn muốn sử dụng chỉ mục phụ toàn cầu trên trường "CreatedAt".

Để biết thêm về chỉ mục phụ DynamoDB, hãy xem tài liệu chỉ mục phụ

Câu trả lời gốc:

DynamoDB không chỉ cho phép tra cứu được lập chỉ mục trên khóa phạm vi. Cần có khóa băm để dịch vụ biết phân vùng nào cần tìm để tìm dữ liệu.

Tất nhiên, bạn có thể thực hiện thao tác quét để lọc theo giá trị ngày, tuy nhiên thao tác này sẽ yêu cầu quét toàn bộ bảng, vì vậy nó không phải là lý tưởng.

Nếu bạn cần thực hiện tra cứu các bản ghi được lập chỉ mục theo thời gian trên nhiều khóa chính, DynamoDB có thể không phải là dịch vụ lý tưởng để bạn sử dụng hoặc bạn có thể cần sử dụng một bảng riêng biệt (trong DynamoDB hoặc một cửa hàng quan hệ) để lưu trữ mục siêu dữ liệu mà bạn có thể thực hiện tra cứu được lập chỉ mục.


14
Xem các bình luận về câu trả lời bên dưới; hiện không có cách nào để xử lý vấn đề này, ít nhất là không phải vì những gì OP yêu cầu. GSI vẫn yêu cầu bạn chỉ định khóa băm, vì vậy bạn không thể truy vấn tất cả các bản ghi có CreatedAtlớn hơn một điểm nhất định.
pkaeding

4
@pkaeding là đúng. Bạn có thể lấy các bản ghi cũ hơn sau đó đến một số ngày cụ thể bằng cách sử dụng quét , nhưng bạn không thể sắp xếp chúng theo thứ tự. GSI sẽ không giúp bạn trong trường hợp này. Không thể sắp xếp khóa phân vùng , cũng như không thể chỉ truy vấn khóa phạm vi .
gkiko

15
Đối với những bạn bối rối. CÂU TRẢ LỜI NÀY LÀ SAI. Câu trả lời ban đầu của anh ấy là đúng nhưng câu trả lời cập nhật của anh ấy thì không. Đọc câu trả lời của Warren Parad dưới đây. Đúng.
Ryan Shillington

1
@MikeBrant Tôi muốn truy vấn (không quét, xem xét mọi mục trong bảng, làm cho nó rất kém hiệu quả và tốn kém) một bảng trên khóa băm GSI của bảng (CreatedAt) bằng cách sử dụng ký hiệu lớn hơn. Theo như tôi biết, điều này không thể được thực hiện.
Aziz Javed

4
Vấn đề mà bạn có thể gặp phải khi sử dụng ngày làm phân vùng chính là bạn có thể tạo một điểm phát sóng trên một số hoặc một trong số các máy ngang hàng, do thực tế là trong hầu hết các kho lưu trữ dữ liệu, dữ liệu mới được truy vấn thường xuyên hơn dữ liệu cũ.
Kiến thức

53

Với cấu trúc bảng hiện tại của bạn, điều này hiện không thể thực hiện được trong DynamoDB. Thách thức lớn là phải hiểu rằng khóa Hash của bảng (phân vùng) phải được coi như tạo các bảng riêng biệt. Theo một số cách, điều này thực sự mạnh mẽ (hãy nghĩ về các khóa phân vùng như tạo một bảng mới cho mỗi người dùng hoặc khách hàng, v.v.).

Các truy vấn chỉ có thể được thực hiện trong một phân vùng duy nhất. Đó thực sự là kết thúc của câu chuyện. Điều này có nghĩa là nếu bạn muốn truy vấn theo ngày (bạn sẽ muốn sử dụng msec kể từ kỷ nguyên), thì tất cả các mục bạn muốn truy xuất trong một truy vấn phải có cùng một Hash (khóa phân vùng).

Tôi nên đủ điều kiện này. Bạn hoàn toàn có thể làm scantheo tiêu chí bạn đang tìm kiếm, điều đó không có vấn đề gì, nhưng điều đó có nghĩa là bạn sẽ xem xét từng hàng đơn lẻ trong bảng của mình và sau đó kiểm tra xem hàng đó có ngày phù hợp với thông số của bạn hay không. Điều này thực sự tốn kém, đặc biệt nếu bạn đang kinh doanh lưu trữ các sự kiện theo ngày ngay từ đầu (tức là bạn có rất nhiều hàng.)

Bạn có thể bị cám dỗ để đặt tất cả dữ liệu vào một phân vùng duy nhất để giải quyết vấn đề, và bạn hoàn toàn có thể làm được, tuy nhiên thông lượng của bạn sẽ rất thấp, do mỗi phân vùng chỉ nhận được một phần nhỏ trong tổng số lượng đã đặt.

Điều tốt nhất cần làm là xác định các phân vùng hữu ích hơn để tạo để lưu dữ liệu:

  • Bạn có thực sự cần xem xét tất cả các hàng hay chỉ những hàng của một người dùng cụ thể?

  • Đầu tiên bạn có thể thu hẹp danh sách theo Tháng và thực hiện nhiều truy vấn (một truy vấn cho mỗi tháng) không? Hay theo năm?

  • Nếu bạn đang thực hiện phân tích chuỗi thời gian, có một số tùy chọn, hãy thay đổi khóa phân vùng thành một cái gì đó được tính toán PUTđể querydễ dàng hơn hoặc sử dụng một sản phẩm aws khác như kinesis cho phép ghi nhật ký chỉ thêm vào.


4
Tôi muốn nhấn mạnh lựa chọn mà bạn đưa ra trong đoạn cuối cùng về việc cân nhắc "theo năm". Tạo một thuộc tính như yyyyvà băm trên đó, nhưng cũng tạo một createdngày bạn có thể sử dụng làm khóa phạm vi của mình. Sau đó, bạn nhận được 10GB dữ liệu mỗi năm (27 MB mỗi ngày), điều này có thể tốt cho nhiều trường hợp hơn. Điều đó có nghĩa là bạn phải tạo truy vấn mỗi năm khi truy vấn ngày vượt qua ranh giới năm, nhưng ít nhất nó sẽ hoạt động và an toàn hơn so với việc tạo khóa băm giả.
Ryan Shillington


1
như liên kết ở trên giải thích, các khóa phân vùng dựa trên thời gian nghiêm ngặt có thể dẫn đến các điểm nóng. nếu bạn phải sử dụng khóa phân vùng dựa trên thời gian, tốt hơn nên thêm một số phần tử khác vào khóa phân vùng để dàn trải một khoảng thời gian trên nhiều phân vùng. Tôi đã thấy các đề xuất về việc chỉ sử dụng tiền tố giữa 0-n trong đó n là số lượng phân vùng mỗi lần nhóm nên được trải rộng.
dres

@RyanShillington Không có giới hạn 10GB cho các chỉ mục phụ toàn cầu . Giới hạn đó chỉ áp dụng cho các chỉ mục phụ cục bộ .
Simon Forsberg

18

Phương pháp tiếp cận mà tôi đã thực hiện để giải quyết vấn đề này là tạo Chỉ mục phụ toàn cầu như bên dưới. Không chắc liệu đây có phải là cách tốt nhất hay không nhưng hy vọng nếu nó hữu ích cho ai đó.

Hash Key                 | Range Key
------------------------------------
Date value of CreatedAt  | CreatedAt

Giới hạn áp dụng cho người dùng API HTTP để chỉ định số ngày truy xuất dữ liệu, được đặt mặc định là 24 giờ.

Bằng cách này, tôi luôn có thể chỉ định HashKey là Ngày của ngày hiện tại và RangeKey có thể sử dụng toán tử> và <trong khi truy xuất. Bằng cách này, dữ liệu cũng được trải rộng trên nhiều phân đoạn.


8

Khóa băm của bạn (chính của loại) phải là duy nhất (trừ khi bạn có một phạm vi như những người khác đã nêu).

Trong trường hợp của bạn, để truy vấn bảng của bạn, bạn nên có một chỉ mục phụ.

|  ID  | DataID | Created | Data |
|------+--------+---------+------|
| hash | xxxxx  | 1234567 | blah |

Khóa băm của bạn là ID Chỉ mục phụ của bạn được định nghĩa là: DataID-Created-index (đó là tên mà DynamoDB sẽ sử dụng)

Sau đó, bạn có thể thực hiện một truy vấn như sau:

var params = {
    TableName: "Table",
    IndexName: "DataID-Created-index",
    KeyConditionExpression: "DataID = :v_ID AND Created > :v_created",
    ExpressionAttributeValues: {":v_ID": {S: "some_id"},
                                ":v_created": {N: "timestamp"}
    },
    ProjectionExpression: "ID, DataID, Created, Data"
};

ddb.query(params, function(err, data) {
    if (err) 
        console.log(err);
    else {
        data.Items.sort(function(a, b) {
            return parseFloat(a.Created.N) - parseFloat(b.Created.N);
        });
        // More code here
    }
});

Về cơ bản truy vấn của bạn trông giống như:

SELECT * FROM TABLE WHERE DataID = "some_id" AND Created > timestamp;

Chỉ số phụ sẽ tăng đơn vị khả năng đọc / ghi theo yêu cầu nên bạn cần cân nhắc điều đó. Nó vẫn tốt hơn rất nhiều so với quét, sẽ tốn kém về thời gian và thời gian đọc (và tôi tin rằng giới hạn trong 100 mục).

Đây có thể không phải là cách tốt nhất để làm điều đó nhưng đối với một người đã từng sử dụng RD (tôi cũng đã quen với SQL) thì đó là cách nhanh nhất để làm việc hiệu quả. Vì không có ràng buộc nào liên quan đến lược đồ, bạn có thể tạo ra một thứ gì đó hoạt động và một khi bạn có băng thông để làm việc theo cách hiệu quả nhất, bạn có thể thay đổi mọi thứ.


1
Bạn nói rằng không có ràng buộc nào, nhưng bạn nên biết rằng cách tiếp cận này có nghĩa là bạn có thể tiết kiệm tối đa 10GB dữ liệu (tối đa của một phân vùng duy nhất).
Ryan Shillington

Đây sẽ là cách tiếp cận nếu DataID được biết đến. Nhưng ở đây chúng ta cần lấy mọi hàng mà hàng được tạo hơn một ngày nào đó.
Yasith Prabuddhaka

3

Bạn có thể tạo khóa băm nào đó dọc theo các dòng của id 'danh mục sản phẩm', sau đó là khóa phạm vi dưới dạng kết hợp của dấu thời gian với id duy nhất được thêm vào cuối. Bằng cách đó, bạn biết khóa băm và vẫn có thể truy vấn ngày với số lớn hơn.


1

Bạn có thể có nhiều khóa băm giống hệt nhau; nhưng chỉ khi bạn có một khóa phạm vi thay đổi. Hãy nghĩ về nó giống như các định dạng tệp; bạn có thể có 2 tệp trùng tên trong cùng một thư mục miễn là định dạng của chúng khác nhau. Nếu định dạng của chúng giống nhau, tên của chúng phải khác nhau. Khái niệm tương tự cũng áp dụng cho các khóa băm / phạm vi của DynamoDB; chỉ cần nghĩ về băm là tên và phạm vi là định dạng.

Ngoài ra, tôi không nhớ họ có những thứ này vào thời điểm OP hay không (tôi không tin là họ đã làm), nhưng họ hiện cung cấp Chỉ mục phụ cục bộ.

Sự hiểu biết của tôi về những điều này là giờ đây nó sẽ cho phép bạn thực hiện các truy vấn mong muốn mà không cần phải quét toàn bộ. Nhược điểm là các chỉ mục này phải được chỉ định khi tạo bảng và (tôi tin rằng) không được để trống khi tạo một mục. Ngoài ra, chúng yêu cầu thông lượng bổ sung (mặc dù thường không nhiều như quét) và bộ nhớ, vì vậy, đây không phải là một giải pháp hoàn hảo, nhưng là một giải pháp thay thế khả thi đối với một số người.

Tuy nhiên, tôi vẫn khuyên câu trả lời của Mike Brant là phương pháp sử dụng DynamoDB ưa thích; và tự mình sử dụng phương pháp đó. Trong trường hợp của tôi, tôi chỉ có một bảng trung tâm chỉ có khóa băm làm ID của tôi, sau đó các bảng phụ có hàm băm và phạm vi có thể được truy vấn, sau đó mục này trỏ mã đến "mục quan tâm" của bảng trung tâm, trực tiếp .

Dữ liệu bổ sung về các chỉ mục phụ có thể được tìm thấy trong tài liệu DynamoDB của Amazon tại đây cho những người quan tâm.

Dù sao, hy vọng điều này sẽ giúp ích cho bất kỳ ai khác xảy ra trên chủ đề này.


Tôi đã thử tạo một bảng DynamoDB trong đó có AWSDynamoDBKeySchemaElement 'createAt' thuộc loại băm và một lần nữa AWSDynamoDBKeySchemaElement 'createdAt' của phạm vi loại và tôi gặp lỗi cho biết Error Domain = com.amazonaws.AWSDynamoDBErrorDomain Code) = 0 " = {__ type = com.amazon.coral.validate # ValidationException, message = Cả Hash Key và phần tử Range Key trong KeySchema đều có cùng tên}. Vì vậy, tôi không nghĩ những gì bạn đang nói là chính xác.
user1709076

Tôi tin rằng bạn đã hiểu lầm (mặc dù tôi cho rằng tôi cũng không rõ ràng trong mô tả của mình). Bạn không thể có 2 thuộc tính (cột) khác nhau có cùng tên trong một bảng, nhưng khi bạn tạo khóa băm với khóa phạm vi, bạn có thể có nhiều mục sử dụng cùng một hàm băm miễn là phạm vi của chúng khác nhau và ngược lại. Ví dụ: Hàm băm của bạn là "ID" và phạm vi của bạn là "Ngày", bạn có thể có 2 phiên bản của ID "1234" miễn là Ngày của chúng khác nhau.
DGolberg

Ah DGoldberg! Tôi có được bạn ngay bây giờ. Thật tuyệt. Vì vậy, đối với trường hợp của tôi vì tôi chỉ và luôn muốn truy vấn tin nhắn văn bản 'after date = x', có vẻ như tôi có thể đặt tất cả các tin nhắn văn bản có cùng một 'fake_hash = 1'. Sau đó, thực hiện truy vấn của tôi.keyConditionExpression = @ "fake_hash = 1 và # Ngày>: val". Cảm ơn rât nhiều. Nếu bạn có bất kỳ đầu vào nào khác, tôi rất vui khi biết điều đó vì có vẻ kỳ lạ khi có một băm luôn có cùng giá trị?
user1709076

Tôi phải kiểm tra lại, nhưng tôi khá chắc chắn rằng bạn có thể thực hiện một truy vấn trên các bảng chỉ có băm ... mặc dù nếu bạn đang sử dụng dấu ngày / giờ làm dấu băm của mình, tôi khuyên bạn nên ghi lại vào đơn vị ngắn nhất có thể, như mili giây hoặc nano / micro giây (bất kể đơn vị thời gian nhỏ nhất mà mã có thể ghi là gì), để giảm nguy cơ trùng lặp ngày / giờ. Ngoài ra, bạn có thể thêm khóa lạc quan để giảm thêm khả năng chồng chéo: docs.aws.amazon.com/amazondynamodb/latest/developerguide/… Chỉ cần thử lại vào lần khác nếu có xung đột.
DGolberg

-11

Câu trả lời đã cập nhật Không có cách nào thuận tiện để làm điều này bằng cách sử dụng Dynamo DB Queries với thông lượng có thể dự đoán được. Một tùy chọn (tối ưu phụ) là sử dụng GSI với HashKey & CreatedAt nhân tạo. Sau đó, truy vấn bằng HashKey một mình và đề cập đến ScanIndexForward để sắp xếp kết quả. Nếu bạn có thể tạo ra một HashKey tự nhiên (giả sử danh mục của mặt hàng, v.v.) thì phương pháp này là người chiến thắng. Mặt khác, nếu bạn giữ cùng một HashKey cho tất cả các mục thì nó sẽ ảnh hưởng chủ yếu đến thông lượng khi tập dữ liệu của bạn phát triển vượt quá 10GB (một phân vùng)

Câu trả lời gốc: Bạn có thể thực hiện việc này ngay bây giờ trong DynamoDB bằng cách sử dụng GSI. Đặt trường "CreatedAt" làm GSI và đưa ra các truy vấn như (GT some_date). Lưu trữ ngày tháng dưới dạng số (mili giây kể từ kỷ nguyên) cho loại truy vấn này.

Thông tin chi tiết có tại đây: Chỉ mục phụ toàn cầu - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Using

Đây là một tính năng rất mạnh mẽ. Lưu ý rằng truy vấn được giới hạn ở (EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN) Điều kiện - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html


31
Tôi đã phản đối vì theo như tôi có thể nói, câu trả lời của bạn không chính xác. Giống như khóa chính của bảng, bạn có thể truy vấn khóa băm của GSI chỉ với toán tử EQ. Nếu bạn đang ngụ ý rằng đó CreatedAtphải là khóa phạm vi của GSI, thì bạn sẽ cần chọn khóa băm - và sau đó bạn quay lại nơi bạn bắt đầu, vì bạn sẽ chỉ có thể truy vấn GT CreatedAtcho một giá trị cụ thể của khóa băm.
PaF

Đồng ý với PaF. Sử dụng GSI với khóa băm vì thời gian tạo không giúp ích cho câu hỏi được hỏi trong OP.
4-8-15-16-23-42
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.