DynamoDB'yi tarihe göre sorgulama


107

İlişkisel bir veritabanı geçmişinden geliyorum ve Amazon'un DynamoDB'si ile çalışmaya çalışıyorum

Karma anahtarı "DataID" ve "CreatedAt" aralığı ve içinde bir sürü öğe olan bir tablom var.

Belirli bir tarihten sonra oluşturulan ve tarihe göre sıralanmış tüm öğeleri almaya çalışıyorum. İlişkisel bir veri tabanında bu oldukça basittir.

DynamoDB'de bulabildiğim en yakın şey bir sorgu ve filtreden daha büyük aralık anahtarını kullanmaktır. Tek sorun, bir sorgu gerçekleştirmek için amacı ortadan kaldıran bir hash anahtarına ihtiyacım olmasıdır.

Öyleyse neyi yanlış yapıyorum? Tablo şemam yanlış mı, hash anahtarının benzersiz olması gerekmez mi? yoksa sorgulamanın başka bir yolu var mı?

Yanıtlar:


34

Güncellenmiş Cevap:

DynamoDB, bu tür bir sorgulamaya yardımcı olmak için ikincil dizinlerin belirlenmesine izin verir. İkincil dizinler ya global olabilir, yani indeksin karma anahtarlar boyunca tüm tabloyu kapsadığı anlamına gelir ya da dizinin her bir karma anahtar bölümü içinde var olacağı anlamına gelir, dolayısıyla karma anahtarın sorgu yapılırken de belirtilmesi gerekir.

Bu sorunun kullanım örneği için, "CreatedAt" alanında genel bir ikincil dizin kullanmak istersiniz.

DynamoDB ikincil dizinler hakkında daha fazla bilgi için ikincil dizin belgelerine bakın

Orijinal Cevap:

DynamoDB, yalnızca aralık anahtarında dizine eklenen aramalara izin vermez. Karma anahtar, hizmetin verileri bulmak için hangi bölüme bakacağını bilmesi için gereklidir.

Tabii ki tarih değerine göre filtrelemek için bir tarama işlemi gerçekleştirebilirsiniz, ancak bu tam bir tablo taraması gerektirir, bu nedenle ideal değildir.

Birden çok birincil anahtarda zamana göre dizine alınmış kayıt araması yapmanız gerekiyorsa, DynamoDB kullanmanız için ideal bir hizmet olmayabilir veya öğeyi depolamak için ayrı bir tablo (ya DynamoDB'de ya da ilişkisel bir depoda) kullanmanız gerekebilir. dizinlenmiş bir arama gerçekleştirebileceğiniz meta veriler.


15
Aşağıdaki cevapla ilgili yorumlara bakın; orada değil , en azından OP ne sordu için, Artık bununla yolları. GSI'lar yine de bir karma anahtar belirtmenizi gerektirir, bu nedenle CreatedAtbelirli bir noktadan daha büyük tüm kayıtları sorgulayamazsınız .
pkaeding

4
@pkaeding doğru. Tarama özelliğini kullanarak belirli bir tarihten daha eski kayıtları alabilirsiniz , ancak bunları sıralı sırada alamazsınız. GSI bu durumda size yardımcı olmayacak. Bölüm anahtarını sıralamak mümkün olmadığı gibi, yalnızca aralık anahtarını sorgulamak da mümkün değildir .
gkiko

15
Kafanız karışanlar için. BU CEVAP YANLIŞ. Orijinal cevabı doğru, ancak güncellenmiş cevabı değil. Aşağıda Warren Parad'ın cevabını okuyun. Bu doğru.
Ryan Shillington

1
@MikeBrant Büyüktür sembolünü kullanarak bir tablonun GSI hash anahtarındaki (CreatedAt) bir tabloyu sorgulamak istiyorum (tablodaki her maddeye bakan, onu çok verimsiz ve maliyetli yapan taramak değil). Bildiğim kadarıyla bu yapılamaz.
azizj1

4
Bir tarihi birincil bölüm olarak kullanırken muhtemelen karşılaştığınız sorun , çoğu veri deposunda yeni verilerin eski verilere göre daha sık sorgulanması nedeniyle eşlerin bir kısmında veya bazılarında bir sıcak nokta oluşturabilmenizdir.
Bilgi

54

Mevcut tablo yapınız göz önüne alındığında, bu şu anda DynamoDB'de mümkün değildir. En büyük zorluk, tablonun (bölüm) Hash anahtarının ayrı tablolar oluşturuyor gibi ele alınması gerektiğini anlamaktır. Bazı yönlerden bu gerçekten güçlüdür (bölüm anahtarlarını her kullanıcı veya müşteri için yeni bir tablo oluştururken düşünün, vb.

Sorgular yalnızca tek bir bölümde yapılabilir. Bu gerçekten hikayenin sonu. Bu, tarihe göre sorgulamak istiyorsanız (epoch'tan beri msec kullanmak isteyeceksiniz), tek bir sorguda geri almak istediğiniz tüm öğelerin aynı Hash'e (bölüm anahtarı) sahip olması gerektiği anlamına gelir.

Bunu nitelendirmeliyim. scanAradığınız kritere göre kesinlikle yapabilirsiniz , bu sorun değil, ancak bu, tablonuzdaki her satıra bakacağınız ve ardından bu satırın parametrelerinize uyan bir tarih olup olmadığını kontrol edeceğiniz anlamına gelir. Bu gerçekten pahalıdır, özellikle olayları ilk etapta tarihe göre saklama işindeyseniz (yani çok sayıda satırınız varsa).

Sorunu çözmek için tüm verileri tek bir bölüme koymak isteyebilirsiniz ve kesinlikle yapabilirsiniz, ancak her bölümün toplam ayarlanan miktarın yalnızca bir kısmını aldığı göz önüne alındığında veriminiz acı verici derecede düşük olacaktır.

Yapılacak en iyi şey, verileri kaydetmek için oluşturmak üzere daha kullanışlı bölümler belirlemektir:

  • Gerçekten tüm satırlara bakmanız mı gerekiyor yoksa bu yalnızca belirli bir kullanıcının satırları mı?

  • Listeyi önce Ay'a göre daraltmak ve birden çok sorgu (her ay için bir tane) yapmak uygun olur mu? Veya Yıla göre?

  • Zaman serisi analizi yapıyorsanız, birkaç seçenek vardır, daha kolay PUThale getirmek için bölüm anahtarını üzerinde hesaplanan bir şeyle değiştirin queryya da kinesis gibi yalnızca ekleme-günlüğe izin veren başka bir aws ürünü kullanın.


4
Son paragrafınızda öne sürdüğünüz "yıl bazında" düşünme seçeneğinin altını çizmek istiyorum. Buna benzer yyyyve karma bir özellik oluşturun , ancak aynı zamanda createdaralık anahtarınız olarak kullanabileceğiniz bir tarih oluşturun. Daha sonra yılda 10 GB veri (günde 27 MB) alırsınız, bu muhtemelen daha fazla koşul için uygundur. Bu, tarih sorguları yıl sınırını aştığında her yıl bir sorgu oluşturmanız gerektiği anlamına gelir, ancak en azından işe yarayacaktır ve bir kukla karma anahtar oluşturmaktan daha güvenlidir.
Ryan Shillington


1
Yukarıdaki bağlantının açıkladığı gibi, kesinlikle zamana dayalı bölüm anahtarları sorunlu noktalara yol açabilir. zamana dayalı bölüm anahtarları kullanmanız gerekiyorsa, bir zaman aralığını birden çok bölüme yaymak için bölüm anahtarına başka bir öğe eklemek daha iyidir. Sadece 0-n arasında bir önek kullanma önerileri gördüm, burada n, her bir bölümün üzerine yayılması gereken bölüm sayısıdır.
dres

@RyanShillington üzerinde hiçbir 10GB sınırı yoktur küresel ikincil endeksler. Bu sınır yalnızca yerel ikincil dizinler için geçerlidir .
Simon Forsberg

18

Bu sorunu çözmek için izlediğim yaklaşım aşağıdaki gibi bir Küresel İkincil Endeks oluşturmaktır. Bunun en iyi yaklaşım olduğundan emin değilim, ama umarım birisi için yararlıdır.

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

Verilerin alınacağı gün sayısını belirleme için HTTP API kullanıcısına uygulanan sınırlama, varsayılan olarak 24 saattir.

Bu şekilde, HashKey'i her zaman Current date's day olarak belirtebilirim ve RangeKey, alırken> ve <operatörlerini kullanabilir. Bu şekilde veriler birden çok parçaya da yayılır.


8

Hash anahtarınız (sıralamanın birincil) benzersiz olmalıdır (başkalarının belirttiği gibi bir aralığınız yoksa).

Sizin durumunuzda, tablonuzu sorgulamak için ikincil bir dizine sahip olmanız gerekir.

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

Karma Anahtarınız Kimliktir İkincil dizininiz şu şekilde tanımlanır: Veri Kimliği Oluşturulan dizin (DynamoDB'nin kullanacağı ad budur)

Daha sonra şöyle bir sorgu yapabilirsiniz:

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

Esasen sorgunuz şöyle görünür:

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

İkincil Dizin, gerekli okuma / yazma kapasitesi birimlerini artıracağından, bunu dikkate almanız gerekir. Yine de bir tarama yapmaktan çok daha iyi, okuma ve zaman açısından maliyetli olacak (ve inandığım 100 öğeyle sınırlı).

Bunu yapmanın en iyi yolu bu olmayabilir, ancak RD'ye alışkın biri için (ben de SQL'e alışkınım) üretken olmanın en hızlı yolu budur. Şema ile ilgili herhangi bir kısıtlama olmadığından, çalışan bir şeyi kırbaçlayabilirsiniz ve en verimli şekilde çalışmak için bant genişliğine sahip olduğunuzda, etrafındaki şeyleri değiştirebilirsiniz.


1
Hiçbir kısıtlama olmadığını söylüyorsunuz, ancak bu yaklaşımın en fazla 10GB veriden tasarruf edebileceğiniz anlamına geldiğini bilmelisiniz (en fazla tek bölüm).
Ryan Shillington

DataID biliniyorsa yaklaşım bu olurdu. Ama burada yaratılanın bir tarihten daha fazla olduğu her satırı almamız gerekiyor.
Yasith Prabuddhaka

3

Hash anahtarını bir 'ürün kategorisi' kimliğinin satırları boyunca bir şey yapabilir, ardından aralık anahtarını bir zaman damgası ile sonuna eklenen benzersiz bir kimlik kombinasyonu olarak yapabilirsiniz. Bu şekilde, hash anahtarını bilirsiniz ve hala 'dan büyük olan tarihi sorgulayabilirsiniz.


1

Birden çok özdeş karma anahtarınız olabilir; ancak yalnızca değişen bir aralık anahtarınız varsa. Dosya formatları gibi düşünün; Biçimleri farklı olduğu sürece, aynı klasörde aynı ada sahip 2 dosyaya sahip olabilirsiniz. Biçimleri aynıysa, adları farklı olmalıdır. Aynı kavram DynamoDB'nin karma / aralık anahtarları için de geçerlidir; hash'i isim olarak ve aralığı format olarak düşünün.

Ayrıca, OP sırasında bunlara sahip olup olmadıklarını hatırlamıyorum (yaptıklarına inanmıyorum), ancak şimdi Yerel İkincil Endeksler sunuyorlar.

Bunları anladığım, artık tam bir tarama yapmanıza gerek kalmadan istenen sorguları gerçekleştirmenize izin vermesi gerektiğidir. Dezavantajı, bu dizinlerin tablo oluştururken belirtilmesi gerekmesi ve ayrıca (inanıyorum) bir öğe oluştururken boş bırakılamayacak olmasıdır. Ek olarak, ek verim (tipik olarak bir tarama kadar olmasa da) ve depolamaya ihtiyaç duyarlar, bu nedenle bu, bazıları için mükemmel bir çözüm değil, uygulanabilir bir alternatiftir.

Yine de DynamoDB'yi kullanmanın tercih edilen yöntemi olarak Mike Brant'ın yanıtını öneriyorum; ve bu yöntemi kendim kullanıyorum. Benim durumumda, kimliğim olarak yalnızca bir karma anahtarı olan merkezi bir tablom, ardından sorgulanabilen bir karma ve aralığa sahip ikincil tablolar var, ardından öğe kodu doğrudan merkezi tablonun "ilgilenilen öğeye" işaret ediyor .

İkincil dizin ilgili Ek veri Amazon'un DynamoDB belgelerinde bulunabilir burada ilgilenenler için.

Her neyse, umarım bu, bu ileti dizisinde olan başka herkese yardımcı olur.


Karma türünde AWSDynamoDBKeySchemaElement 'createdAt' ve yine tür aralığının AWSDynamoDBKeySchemaElement 'createdAt' olduğu bir DynamoDB tablosu oluşturmayı denedim ve Error Domain = com.amazonaws.AWSDynamoDBErrorDomain Code = 0 "yazan bir hata aldım = {__ type = com.amazon.coral.validate # ValidationException, message = KeySchema'daki Hem Karma Anahtarı hem de Aralık Anahtarı öğesi aynı ada sahip}. Bu yüzden söylediklerinin doğru olduğunu sanmıyorum.
user1709076

Yanlış anladığınıza inanıyorum (açıklamamda da çok net değildim sanırım). Bir tabloda aynı ada sahip 2 farklı özelliğe (sütun) sahip olamazsınız, ancak bir aralık anahtarıyla bir karma anahtar oluşturduğunuzda, aralıkları farklı olduğu sürece aynı karmayı kullanan birden çok öğeye sahip olabilirsiniz ve tam tersi. Örneğin: Karmanız "Kimlik" ve aralığınız "Tarih", Tarihi farklı olduğu sürece "1234" kimliğinin 2 örneğine sahip olabilirsiniz.
DGolberg

Ah DGoldberg! Seni şimdi anlıyorum. Bu harika. Bu yüzden benim durumum için sadece ve her zaman metin mesajlarını 'tarih = x' sonrası sorgulamak istediğim için, tüm metin mesajlarını aynı 'fake_hash = 1' olacak şekilde ayarlayabilirim gibi görünüyor. Ardından query.keyConditionExpression = @ "fake_hash = 1 ve #Date>: val" sorgumu yap. Çok teşekkür ederim. Başka bir girdiniz varsa, her zaman aynı değere sahip bir hash'e sahip olmak garip göründüğü için bunu duymaktan mutluluk duyarım.
user1709076

Tekrar kontrol etmem gerekir, ancak yalnızca karma tablolarda bir sorgu yapabileceğinizden oldukça eminim ... ancak karma değeriniz olarak bir tarih / saat damgası kullanıyorsanız, örtüşen tarih / saat olasılığını azaltmak için mümkün olan en kısa birim, örneğin milisaniye veya nano / mikrosaniye (kodun kaydedebileceği en küçük zaman birimi ne olursa olsun). Ek olarak, çakışma olasılığını daha da azaltmak için iyimser kilitleme ekleyebilirsiniz: docs.aws.amazon.com/amazondynamodb/latest/developerguide/… Bir çelişki varsa başka bir zaman tekrar deneyin.
DGolberg

-11

Güncellenmiş Cevap Öngörülebilir verim ile Dynamo DB Sorguları kullanarak bunu yapmanın uygun bir yolu yoktur. Bir (optimal altı) seçenek, yapay bir HashKey ve CreatedAt ile GSI kullanmaktır. Ardından, yalnızca HashKey ile sorgulayın ve sonuçları sıralamak için ScanIndexForward'dan bahsedin. Doğal bir HashKey bulabilirseniz (öğenin kategorisini vb. Söyleyin), o zaman bu yöntem kazanır. Öte yandan, tüm öğeler için aynı HashKey'i tutarsanız, veri kümeniz 10GB'nin üzerine çıktığında (bir bölüm) çoğunlukla verimi etkileyecektir.

Orijinal Cevap: Bunu şimdi GSI kullanarak DynamoDB'de yapabilirsiniz. "CreatedAt" alanını GSI olarak yapın ve (GT some_date) gibi sorgular yayınlayın. Bu tür sorgular için tarihi bir sayı (epoch'tan beri milisaniye) olarak kaydedin.

Ayrıntılar burada mevcuttur: Global Secondary Indexes - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Using

Bu çok güçlü bir özellik. Sorgunun (EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN) ile sınırlı olduğunu unutmayın - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html


32
Olumsuz oy verdim çünkü anlayabildiğim kadarıyla cevabınız yanlış. Bir tablonun birincil anahtarına çok benzer şekilde, GSI'nın karma anahtarını yalnızca EQ operatörüyle sorgulayabilirsiniz. Bunun CreatedAtGSI'nın aralık anahtarı olması gerektiğini ima ettiyseniz , bir karma anahtarı seçmeniz gerekir - ve sonra başladığınız yere geri dönersiniz, çünkü GT'yi CreatedAtyalnızca belirli bir değer için sorgulayabilirsiniz . kare tuşu.
PaF

PaF ile anlaştı. Oluşturma zamanı olarak hash anahtarıyla bir GSI kullanmak, OP'de sorulan soruya yardımcı olmaz.
4-8-15-16-23-42
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.