การสืบค้น DynamoDB ตามวันที่


111

ฉันมาจากพื้นหลังฐานข้อมูลเชิงสัมพันธ์และพยายามทำงานกับ DynamoDB ของ amazon

ฉันมีตารางที่มีคีย์แฮช "DataID" และช่วง "CreatedAt" และรายการมากมายอยู่ในนั้น

ฉันกำลังพยายามรับรายการทั้งหมดที่สร้างขึ้นหลังจากวันที่ที่ระบุและจัดเรียงตามวันที่ ซึ่งค่อนข้างตรงไปตรงมาในฐานข้อมูลเชิงสัมพันธ์

ใน DynamoDB สิ่งที่ใกล้เคียงที่สุดที่ฉันพบคือแบบสอบถามและใช้คีย์ช่วงที่มากกว่าตัวกรอง ปัญหาเดียวคือในการดำเนินการค้นหาฉันต้องการคีย์แฮชที่เอาชนะวัตถุประสงค์

แล้วฉันทำอะไรผิด? สคีมาตารางของฉันผิดคีย์แฮชต้องไม่ซ้ำกันหรือไม่ หรือมีวิธีอื่นในการสอบถาม?

คำตอบ:


37

คำตอบที่อัปเดต:

DynamoDB อนุญาตให้มีข้อกำหนดของดัชนีรองเพื่อช่วยในการสืบค้นประเภทนี้ ดัชนีรองอาจเป็นแบบโกลบอลซึ่งหมายความว่าดัชนีครอบคลุมทั้งตารางในคีย์แฮชหรือความหมายเฉพาะที่ดัชนีจะมีอยู่ภายในพาร์ติชันคีย์แฮชแต่ละพาร์ติชันดังนั้นจึงต้องระบุแฮชคีย์เมื่อสร้างคิวรี

สำหรับกรณีการใช้งานในคำถามนี้คุณต้องการใช้ดัชนีรองส่วนกลางในฟิลด์ "CreatedAt"

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับดัชนีรองของ DynamoDB โปรดดูเอกสารประกอบดัชนีรอง

คำตอบเดิม:

DynamoDB ไม่อนุญาตให้มีการค้นหาที่จัดทำดัชนีบนคีย์ช่วงเท่านั้น จำเป็นต้องใช้คีย์แฮชเพื่อให้บริการทราบว่าพาร์ติชันใดที่ต้องค้นหาเพื่อค้นหาข้อมูล

แน่นอนคุณสามารถทำการสแกนเพื่อกรองตามค่าวันที่ได้ แต่จะต้องมีการสแกนแบบเต็มตารางดังนั้นจึงไม่เหมาะอย่างยิ่ง

หากคุณต้องการค้นหาระเบียนที่จัดทำดัชนีตามเวลาในคีย์หลักหลาย ๆ คีย์ DynamoDB อาจไม่ใช่บริการที่เหมาะสำหรับคุณในการใช้งานหรือคุณอาจต้องใช้ตารางแยกต่างหาก (ไม่ว่าจะใน DynamoDB หรือที่เก็บเชิงสัมพันธ์) เพื่อจัดเก็บรายการ ข้อมูลเมตาที่คุณสามารถทำการค้นหาที่จัดทำดัชนีได้


15
ดูความคิดเห็นเกี่ยวกับคำตอบด้านล่าง ตอนนี้ยังไม่มีวิธีจัดการอย่างน้อยก็ไม่ใช่สำหรับสิ่งที่ OP ถาม GSI ยังคงกำหนดให้คุณระบุคีย์แฮชดังนั้นคุณจึงไม่สามารถค้นหาระเบียนทั้งหมดที่มีCreatedAtค่ามากกว่าจุดหนึ่งได้
pkaeding

4
@pkaeding อยู่พอดี. คุณสามารถรับบันทึกที่เก่ากว่าจากวันที่เฉพาะเจาะจงได้โดยใช้การสแกนแต่คุณไม่สามารถจัดเรียงตามลำดับได้ GSI จะไม่ช่วยคุณในกรณีนี้ ไม่สามารถจัดเรียงคีย์พาร์ติชันและไม่สามารถค้นหาเฉพาะคีย์ช่วงได้
gkiko

15
สำหรับพวกคุณสับสน คำตอบนี้ผิด คำตอบเดิมของเขาถูกต้อง แต่คำตอบที่ปรับปรุงแล้วของเขาไม่ใช่ อ่านคำตอบของ Warren Parad ด้านล่าง ถูกต้อง.
Ryan Shillington

1
@ MikeBrant ฉันต้องการสอบถาม (ไม่ใช่สแกนซึ่งดูทุกรายการในตารางทำให้ไม่มีประสิทธิภาพและเสียค่าใช้จ่ายมาก) ตารางบนคีย์แฮช GSI ของตาราง (CreatedAt) โดยใช้สัญลักษณ์มากกว่า เท่าที่ฉันรู้นี่ไม่สามารถทำได้
azizj1

4
ปัญหาที่คุณอาจได้รับในขณะที่ใช้วันที่เป็นพาร์ติชันหลักคือคุณอาจสร้างฮอตสปอตบนอุปกรณ์บางตัวหรือหนึ่งในเครื่องเดียวกันเนื่องจากในการจัดเก็บข้อมูลส่วนใหญ่ข้อมูลใหม่จะถูกสอบถามบ่อยกว่าข้อมูลเก่า
ความรู้

54

เนื่องจากโครงสร้างตารางปัจจุบันของคุณไม่สามารถทำได้ใน DynamoDB ความท้าทายอย่างมากคือการทำความเข้าใจว่าคีย์ Hash ของตาราง (พาร์ติชัน) ควรถือเป็นการสร้างตารางแยกต่างหาก ในบางวิธีสิ่งนี้มีประสิทธิภาพมาก (ลองนึกถึงพาร์ติชันคีย์เป็นการสร้างตารางใหม่สำหรับผู้ใช้หรือลูกค้าแต่ละราย ฯลฯ ... )

การสืบค้นสามารถทำได้ในพาร์ติชันเดียวเท่านั้น นั่นคือจุดจบของเรื่องจริงๆ ซึ่งหมายความว่าหากคุณต้องการค้นหาตามวันที่ (คุณจะต้องใช้ msec ตั้งแต่ยุค) ดังนั้นรายการทั้งหมดที่คุณต้องการดึงข้อมูลในแบบสอบถามเดียวจะต้องมี Hash (คีย์พาร์ติชัน) เดียวกัน

ฉันควรมีคุณสมบัตินี้ คุณสามารถทำได้scanโดยเกณฑ์ที่คุณกำลังมองหานั่นไม่ใช่ปัญหา แต่นั่นหมายความว่าคุณจะดูทุกแถวในตารางของคุณจากนั้นตรวจสอบว่าแถวนั้นมีวันที่ที่ตรงกับพารามิเตอร์ของคุณหรือไม่ สิ่งนี้มีราคาแพงมากโดยเฉพาะอย่างยิ่งหากคุณทำธุรกิจจัดเก็บกิจกรรมตามวันที่ตั้งแต่แรก (เช่นคุณมีแถวจำนวนมาก)

คุณอาจถูกล่อลวงให้ใส่ข้อมูลทั้งหมดในพาร์ติชันเดียวเพื่อแก้ปัญหาและคุณสามารถทำได้อย่างแน่นอนอย่างไรก็ตามทรูพุตของคุณจะต่ำอย่างเจ็บปวดเนื่องจากแต่ละพาร์ติชันได้รับเพียงเศษเสี้ยวของจำนวนที่ตั้งไว้ทั้งหมด

สิ่งที่ดีที่สุดที่ต้องทำคือกำหนดพาร์ติชั่นที่มีประโยชน์มากขึ้นเพื่อสร้างเพื่อบันทึกข้อมูล:

  • คุณจำเป็นต้องดูแถวทั้งหมดจริง ๆ หรือเป็นเฉพาะแถวโดยผู้ใช้เฉพาะ?

  • ก่อนอื่นจะ จำกัด รายการตามเดือนให้แคบลงและทำแบบสอบถามหลายรายการ (หนึ่งข้อต่อเดือน) ได้หรือไม่ หรือตามปี?

  • หากคุณกำลังทำการวิเคราะห์อนุกรมเวลามีสองตัวเลือกให้เปลี่ยนคีย์พาร์ติชันเป็นสิ่งที่คำนวณPUTเพื่อให้queryง่ายขึ้นหรือใช้ผลิตภัณฑ์ aws อื่นเช่น kinesis ซึ่งยืมตัวเองเพื่อผนวกการบันทึกเท่านั้น


4
ฉันต้องการเน้นตัวเลือกที่คุณระบุไว้ในย่อหน้าสุดท้ายเกี่ยวกับการพิจารณา "ตามปี" สร้างแอตทริบิวต์เช่นyyyyและแฮชในนั้น แต่ยังสร้างcreatedวันที่ที่คุณสามารถใช้เป็นคีย์ช่วงของคุณได้ จากนั้นคุณจะได้รับข้อมูล 10GB ต่อปี (27 MB ต่อวัน) ซึ่งอาจดีสำหรับสถานการณ์อื่น ๆ หมายความว่าคุณต้องสร้างแบบสอบถามต่อปีเมื่อการสืบค้นวันที่เลยขอบเขตของปี แต่อย่างน้อยมันก็ใช้งานได้และปลอดภัยกว่าการสร้างคีย์แฮชหลอก
Ryan Shillington

1
อีกทางเลือกหนึ่ง: stackoverflow.com/questions/35963243/…
Ryan Shillington

1
ตามที่ลิงก์ด้านบนอธิบายไว้ว่าคีย์พาร์ติชันตามเวลาอย่างเคร่งครัดอาจทำให้เกิดฮอตสปอตได้ หากคุณต้องใช้พาร์ติชันคีย์ตามเวลาควรเพิ่มองค์ประกอบอื่น ๆ ลงในคีย์พาร์ติชันเพื่อกระจายช่วงเวลาในหลายพาร์ติชัน ฉันได้เห็นคำแนะนำในการใช้คำนำหน้าระหว่าง 0-n โดยที่ n คือจำนวนพาร์ติชันในแต่ละครั้งที่ควรกระจายที่เก็บข้อมูล
Dres

@RyanShillington ทั่วโลกไม่มีขีด จำกัด 10GBดัชนีรองวงเงินที่จะใช้กับท้องถิ่นดัชนีรอง
Simon Forsberg

"แต่ละพาร์ติชันจะได้รับเพียงเศษเสี้ยวของจำนวนที่ตั้งไว้ทั้งหมด" - นี่ไม่เป็นความจริงอีกต่อไปเนื่องจากกำลังการผลิตปรับตัว สำหรับฉันแล้วดูเหมือนว่าคุณอาจมีแอตทริบิวต์จำลองในตารางที่มีค่าเท่ากันเสมอ จากนั้นมีดัชนีรองส่วนกลางโดยใช้แอตทริบิวต์จำลองเป็นคีย์พาร์ติชันและCreatedAtเป็นคีย์การจัดเรียง จากนั้นคุณสามารถค้นหาตามวันที่ในทุกรายการ ดูเหมือนแฮ็ค แต่มีวิธีที่ดีกว่านี้ไหม
Bennett McElwee

18

แนวทางที่ฉันทำตามเพื่อแก้ปัญหานี้คือการสร้างดัชนีทุติยภูมิทั่วโลกดังต่อไปนี้ ไม่แน่ใจว่านี่เป็นแนวทางที่ดีที่สุดหรือไม่ แต่หวังว่าจะเป็นประโยชน์กับใครบางคน

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

ข้อ จำกัด ที่กำหนดไว้สำหรับผู้ใช้ HTTP API เพื่อระบุจำนวนวันในการดึงข้อมูลโดยมีค่าเริ่มต้นเป็น 24 ชม.

ด้วยวิธีนี้ฉันสามารถระบุ HashKey เป็นวันของวันที่ปัจจุบันได้ตลอดเวลาและ RangeKey สามารถใช้> และ <ตัวดำเนินการขณะดึงข้อมูล ด้วยวิธีนี้ข้อมูลจะกระจายไปตามส่วนต่างๆ


8

คีย์แฮชของคุณ (ลำดับหลัก) ต้องไม่ซ้ำกัน (เว้นแต่คุณจะมีช่วงตามที่ผู้อื่นระบุไว้)

ในกรณีของคุณในการสืบค้นตารางของคุณคุณควรมีดัชนีรอง

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

Hash Key ของคุณคือ ID ดัชนีรองของคุณถูกกำหนดเป็น: DataID-Created-index (นั่นคือชื่อที่ DynamoDB จะใช้)

จากนั้นคุณสามารถสร้างแบบสอบถามดังนี้:

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
    }
});

โดยพื้นฐานแล้วคำถามของคุณมีลักษณะดังนี้:

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

ดัชนีทุติยภูมิจะเพิ่มหน่วยความสามารถในการอ่าน / เขียนที่จำเป็นดังนั้นคุณต้องพิจารณาสิ่งนั้น มันยังดีกว่าการสแกนมากซึ่งจะมีค่าใช้จ่ายสูงในการอ่านและตรงเวลา (และฉันเชื่อว่า จำกัด ไว้ที่ 100 รายการ)

นี่อาจไม่ใช่วิธีที่ดีที่สุดในการทำ แต่สำหรับคนที่เคยใช้ RD (ฉันคุ้นเคยกับ SQL ด้วย) เป็นวิธีที่เร็วที่สุดในการทำให้ได้ประสิทธิผล เนื่องจากไม่มีข้อ จำกัด เกี่ยวกับสคีมาคุณสามารถสร้างสิ่งที่ใช้ได้ผลและเมื่อคุณมีแบนด์วิดท์ในการทำงานอย่างมีประสิทธิภาพสูงสุดแล้วคุณสามารถเปลี่ยนแปลงสิ่งต่างๆรอบตัวได้


1
คุณบอกว่าไม่มีข้อ จำกัด แต่คุณควรรู้ว่าวิธีนี้หมายความว่าคุณสามารถบันทึกข้อมูลได้สูงสุด 10GB (สูงสุดของพาร์ติชันเดียว)
Ryan Shillington

นี่จะเป็นแนวทางหากรู้จัก DataID แต่ที่นี่เราต้องได้ทุกแถวที่สร้างไว้มากกว่าวันที่บางวัน
Yasith Prabuddhaka

3

คุณสามารถสร้างคีย์ Hash ตามบรรทัดของรหัส "หมวดหมู่ผลิตภัณฑ์" จากนั้นคีย์ช่วงเป็นการรวมกันของการประทับเวลาที่มีรหัสเฉพาะต่อท้าย ด้วยวิธีนี้คุณจะทราบคีย์แฮชและยังสามารถสืบค้นวันที่ที่มีค่ามากกว่า


1

คุณสามารถมีแฮชคีย์ที่เหมือนกันได้หลายคีย์ แต่ถ้าคุณมีคีย์ช่วงที่แตกต่างกันไป คิดว่ามันเหมือนกับรูปแบบไฟล์ คุณสามารถมีไฟล์ 2 ไฟล์ที่มีชื่อเดียวกันในโฟลเดอร์เดียวกันได้ตราบเท่าที่รูปแบบไฟล์ต่างกัน หากรูปแบบเหมือนกันชื่อจะต้องแตกต่างกัน แนวคิดเดียวกันนี้ใช้กับคีย์แฮช / ช่วงของ DynamoDB เพียงแค่คิดว่าแฮชเป็นชื่อและช่วงเป็นรูปแบบ

นอกจากนี้ฉันจำไม่ได้ว่าพวกเขามีสิ่งเหล่านี้ในช่วงเวลาของ OP (ฉันไม่เชื่อว่าพวกเขาทำ) แต่ตอนนี้พวกเขามีดัชนีรองในท้องถิ่น

ความเข้าใจของฉันเกี่ยวกับสิ่งเหล่านี้คือตอนนี้ควรช่วยให้คุณสามารถดำเนินการค้นหาที่ต้องการได้โดยไม่ต้องทำการสแกนทั้งหมด ข้อเสียคือต้องระบุดัชนีเหล่านี้ในการสร้างตารางและ (ฉันเชื่อว่า) ต้องไม่เว้นว่างเมื่อสร้างรายการ นอกจากนี้ยังต้องการปริมาณงานเพิ่มเติม (แม้ว่าโดยทั่วไปจะไม่มากเท่ากับการสแกน) และพื้นที่จัดเก็บข้อมูลดังนั้นจึงไม่ใช่โซลูชันที่สมบูรณ์แบบ แต่เป็นทางเลือกที่ใช้ได้สำหรับบางคน

ฉันยังคงแนะนำคำตอบของ Mike Brant เป็นวิธีที่ต้องการในการใช้ DynamoDB แม้ว่า; และใช้วิธีนั้นเอง ในกรณีของฉันฉันมีเพียงตารางกลางที่มีเพียงคีย์แฮชเป็น ID ของฉันจากนั้นตารางรองที่มีแฮชและช่วงที่สามารถสืบค้นได้จากนั้นรายการจะชี้รหัสไปที่ "รายการที่น่าสนใจ" ของตารางกลางโดยตรง .

ข้อมูลเพิ่มเติมเกี่ยวกับดัชนีรองสามารถพบได้ในเอกสาร DynamoDB ของ Amazon ที่นี่สำหรับผู้ที่สนใจ

อย่างไรก็ตามหวังว่านี่จะช่วยคนอื่น ๆ ที่เกิดขึ้นในกระทู้นี้


ฉันลองสร้างตาราง DynamoDB ที่มี AWSDynamoDBKeySchemaElement 'createdAt' ของประเภทแฮชและอีกครั้ง AWSDynamoDBKeySchemaElement 'createdAt' ของช่วงประเภทและฉันได้รับข้อผิดพลาดที่ระบุว่า Error Domain = com.amazonaws.AWSDynamoDBErrorDomain Code = 0 "(null)" UserInfo = {__ type = com.amazon.coral.validate # ValidationException, message = ทั้ง Hash Key และองค์ประกอบ Range Key ใน KeySchema มีชื่อเดียวกัน} ดังนั้นฉันไม่คิดว่าสิ่งที่คุณพูดนั้นถูกต้อง
user1709076

ฉันเชื่อว่าคุณเข้าใจผิด (แม้ว่าฉันคิดว่าฉันไม่ชัดเจนในคำอธิบายของฉันเช่นกัน) คุณไม่สามารถมีแอตทริบิวต์ 2 รายการ (คอลัมน์) ที่มีชื่อเดียวกันในตารางได้ แต่เมื่อคุณสร้างคีย์แฮชด้วยคีย์ช่วงคุณสามารถมีหลายรายการที่ทั้งหมดใช้แฮชเดียวกันได้ตราบเท่าที่ช่วงต่างกันและ หนีบในทางกลับกัน ตัวอย่างเช่นแฮชของคุณคือ "ID" และช่วงของคุณคือ "วันที่" คุณสามารถมี ID "1234" ได้ 2 อินสแตนซ์ตราบเท่าที่วันที่ต่างกัน
DGolberg

อาดีโกลด์เบิร์ก! ฉันรับคุณตอนนี้ เยี่ยมมาก ดังนั้นสำหรับกรณีของฉันเนื่องจากฉันเพียงแค่ต้องการค้นหาข้อความ 'after date = x' เสมอดูเหมือนว่าฉันสามารถตั้งค่าข้อความทั้งหมดให้มี 'fake_hash = 1' เหมือนกัน จากนั้นทำ query.keyConditionExpression = @ "fake_hash = 1 และ #Date>: val" ขอบคุณมาก. หากคุณมีข้อมูลอื่น ๆ ฉันยินดีที่จะรับฟังเนื่องจากดูเหมือนจะแปลกที่มีแฮชที่มีค่าเท่ากันเสมอ?
user1709076

ฉันต้องตรวจสอบอีกครั้ง แต่ฉันค่อนข้างแน่ใจว่าคุณสามารถทำแบบสอบถามบนตารางแฮชอย่างเดียวได้ ... แม้ว่าคุณจะใช้การประทับวันที่ / เวลาเป็นแฮชของคุณฉันขอแนะนำให้บันทึกลงใน หน่วยที่สั้นที่สุดเท่าที่จะเป็นไปได้เช่นมิลลิวินาทีหรือนาโน / ไมโครวินาที (หน่วยเวลาที่เล็กที่สุดที่รหัสสามารถบันทึกได้คืออะไรก็ตาม) เพื่อลดโอกาสของวันที่ / เวลาที่ทับซ้อนกัน นอกจากนี้คุณสามารถเพิ่มการล็อกการมองโลกในแง่ดีเพื่อลดความเป็นไปได้ของการทับซ้อนเพิ่มเติม: docs.aws.amazon.com/amazondynamodb/latest/developerguide/… เพียงลองอีกครั้งหากมีข้อขัดแย้ง
DGolberg

-11

คำตอบที่อัปเดต ไม่มีวิธีที่สะดวกในการดำเนินการนี้โดยใช้แบบสอบถาม Dynamo DB ที่มีปริมาณงานที่คาดเดาได้ ตัวเลือกหนึ่ง (ที่เหมาะสมที่สุด) คือการใช้ GSI กับ HashKey เทียมและ CreatedAt จากนั้นค้นหาโดย HashKey เพียงอย่างเดียวและพูดถึง ScanIndexForward เพื่อสั่งซื้อผลลัพธ์ หากคุณสามารถสร้าง HashKey ตามธรรมชาติได้ (พูดหมวดหมู่ของรายการ ฯลฯ ) วิธีนี้เป็นผู้ชนะ ในทางกลับกันหากคุณเก็บ HashKey ไว้เหมือนกันสำหรับทุกรายการมันจะส่งผลต่อปริมาณงานเป็นส่วนใหญ่เมื่อชุดข้อมูลของคุณเติบโตเกิน 10GB (หนึ่งพาร์ติชัน)

คำตอบเดิม: คุณสามารถทำได้ใน DynamoDB โดยใช้ GSI สร้างช่อง "CreatedAt" เป็น GSI และออกแบบสอบถามเช่น (GT some_date) จัดเก็บวันที่เป็นตัวเลข (มิลลิวินาทีตั้งแต่ยุค) สำหรับการสืบค้นประเภทนี้

ดูรายละเอียดได้ที่นี่: Global Secondary Indexes - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Using

นี่เป็นคุณสมบัติที่ทรงพลังมาก โปรดทราบว่าข้อความค้นหาถูก จำกัด ไว้ที่ (EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN) เงื่อนไข - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html


32
ฉันลดคะแนนลงเพราะเท่าที่ฉันบอกได้คำตอบของคุณไม่ถูกต้อง เช่นเดียวกับคีย์หลักของตารางคุณสามารถค้นหาแฮชคีย์ของ GSI ได้ด้วยตัวดำเนินการ EQ เท่านั้น หากคุณบอกเป็นนัยว่าCreatedAtควรเป็นคีย์ช่วงของ GSI คุณจะต้องเลือกคีย์แฮชจากนั้นคุณก็กลับมาที่จุดเริ่มต้นเพราะคุณจะสามารถสืบค้น GT ได้CreatedAtเฉพาะสำหรับค่าเฉพาะของ แฮชคีย์
PaF

เห็นด้วยกับ PaF. การใช้ GSI กับแฮชคีย์เนื่องจากเวลาในการสร้างไม่ได้ช่วยตอบคำถามใน OP
4-8-15-16-23-42
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.