Elasticsearch, Apache Lucene'yi temel alan açık kaynaklı bir arama ve analiz motorudur. Elasticsearch'ü kullanarak değişiklik verileri yakalama (CDC) verileri üzerine uygulamalar oluştururken, sistemi bir dizindeki mevcut belgelerde sık sık yapılan güncellemeleri veya değişiklikleri yönetecek şekilde tasarlamak isteyeceksiniz.
Bu blogda, tam güncellemeler, kısmi güncellemeler ve komut dosyasıyla yazılmış güncellemeler dahil olmak üzere güncellemeler için mevcut olan farklı seçenekleri inceleyeceğiz. Ayrıca Elasticsearch'te bir belgeyi değiştirirken neler olduğunu ve güncellemelerin sıklığının sistemdeki CPU kullanımını nasıl etkilediğini de tartışacağız.
Sık sık güncellenen kullanım örneklerini daha iyi anlamak için Netflix gibi bir video yayın hizmetine yönelik arama uygulamasına bakalım. Bir kullanıcı bir diziyi, yani "siyasi gerilim"i aradığında, anahtar kelimelere ve diğer meta verilere dayalı bir dizi alakalı sonuçla karşılaşır.
“House of Cards” adlı gösterinin Elasticsearch'teki örnek belgesine bakalım:
{ "name": "House of Cards", "description": "Frank Underwood is a Democrat appointed as the Secretary of State. Along with his wife, he sets out on a quest to seek revenge from the people who betrayed him while successfully rising to supremacy.", "genres": ["drama", "thriller"], "views": 100, }
Arama, Elasticsearch'te name
ve description
tam metin arama alanları olarak kullanacak şekilde yapılandırılabilir. Başlık başına görüntüleme sayısını saklayan views
alanı, içeriği artırmak ve daha popüler programları daha üst sıralara çıkarmak için kullanılabilir. Kullanıcı bir programın veya filmin bir bölümünü her izlediğinde views
alanı artırılır.
Bu arama yapılandırmasını Netflix ölçeğindeki bir uygulamada kullanırken, gerçekleştirilen güncelleme sayısı, Netflix Etkileşim Raporu'na göre kolaylıkla dakika başına milyonları aşabilir. Rapora göre kullanıcılar Ocak'tan Temmuz'a kadar yaklaşık 100 milyar saat içerik izledi. Bölüm veya film başına ortalama izlenme süresinin 15 dakika olduğu varsayıldığında, dakika başına izlenme sayısı ortalama 1,3 milyona ulaşıyor. Yukarıda belirtilen arama yapılandırmasıyla her görünüm, milyon ölçeğinde bir güncelleme gerektirecektir.
Birçok arama ve analiz uygulaması, özellikle CDC verileri üzerine oluşturulduklarında sık sık güncellemeler yaşayabilir.
Aşağıdaki kodla Elasticsearch'te nasıl güncelleme yapılacağına dair genel bir örneği inceleyelim:
- from elasticsearch import Elasticsearch # Connect to your Elasticsearch instance es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID you want to update index_name = 'movies' document_id = 'your_document_id' # Retrieve the current document to get the current 'views' value try: current_doc = es.get(index=index_name, id=document_id) current_views = current_doc['_source']['views'] except Exception as e: print(f"Error retrieving current document: {e}") current_views = 0 # Set a default value if there's an error # Define the update body to increment 'views' by 1 update_body = { "doc": { "views": current_views + 1 # Increment 'views' by 1 } } # Perform the update try: es.update(index=index_name, id=document_id, body=update_body) print("Document updated successfully!") except Exception as e: print(f"Error updating document: {e}")
Elasticsearch'te bir güncelleme gerçekleştirirken, mevcut bir belgeyi değiştirmek için indeks API'sini veya bir belgede kısmi güncelleme yapmak için güncelleme API'sini kullanabilirsiniz.
Dizin API'si belgenin tamamını alır, belgede değişiklikler yapar ve ardından belgeyi yeniden dizine ekler. Güncelleme API'si ile belgenin tamamı yerine değiştirmek istediğiniz alanları göndermeniz yeterlidir. Bu yine de belgenin yeniden indekslenmesine neden olur ancak ağ üzerinden gönderilen veri miktarını en aza indirir. Güncelleme API'si özellikle belge boyutunun büyük olduğu ve belgenin tamamını ağ üzerinden göndermenin zaman alıcı olacağı durumlarda kullanışlıdır.
Python kodunu kullanarak hem indeks API'sinin hem de güncelleme API'sinin nasıl çalıştığını görelim.
from elasticsearch import Elasticsearch # Connect to Elasticsearch es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID index_name = "your_index" document_id = "1" # Retrieve the existing document existing_document = es.get(index=index_name, id=document_id) # Make your changes to the document existing_document["_source"]["field1"] = "new_value1" existing_document["_source"]["field2"] = "new_value2" # Call the index API to perform the full update es.index(index=index_name, id=document_id, body=existing_document["_source"])
Yukarıdaki kodda görebileceğiniz gibi, dizin API'si Elasticsearch'e iki ayrı çağrı gerektirir, bu da kümenizde daha yavaş performansa ve daha fazla yüke neden olabilir.
Kısmi güncellemeler dahili olarak reindex API'sini kullanır, ancak daha iyi performans için yalnızca tek bir ağ çağrısı gerektirecek şekilde yapılandırılmışlardır.
from elasticsearch import Elasticsearch # Connect to Elasticsearch es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID index_name = "your_index" document_id = "1" # Specify the fields to be updated update_fields = { "field1": "new_value1", "field2": "new_value2" } # Use the update API to perform a partial update es.update(index=index_name, id=document_id, body={"doc": update_fields})
Görüntüleme sayısını güncellemek için Elasticsearch'teki güncelleme API'sini kullanabilirsiniz ancak güncelleme API'si tek başına görüntüleme sayısını önceki değere göre artırmak için kullanılamaz. Bunun nedeni, yeni görüntüleme sayısı değerini ayarlamak için eski görüntüleme sayısına ihtiyacımız olmasıdır.
Güçlü bir kodlama dili olan Painless'ı kullanarak bunu nasıl düzeltebileceğimizi görelim.
Painless, Elasticsearch için tasarlanmış bir kodlama dilidir ve sorgu ve toplama hesaplamaları, karmaşık koşullar, veri dönüşümleri ve daha fazlası için kullanılabilir. Painless ayrıca karmaşık mantığa dayalı olarak belgeleri değiştirmek için güncelleme sorgularında komut dosyalarının kullanılmasına da olanak tanır.
Aşağıdaki örnekte, tek bir API çağrısında güncelleme gerçekleştirmek ve eski görüntüleme sayısının değerine göre yeni görüntüleme sayısını artırmak için Painless komut dosyasını kullanıyoruz.
from elasticsearch import Elasticsearch # Connect to your Elasticsearch instance es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID you want to update index_name = 'movies' document_id = 'your_document_id' # Define the Painless script for the update update_script = { "script": { "lang": "painless", "source": "ctx._source.views += 1" # Increment 'views' by 1 } } # Perform the update using the Painless script try: es.update(index=index_name, id=document_id, body=update_script) print("Document updated successfully!") except Exception as e: print(f"Error updating document: {e}")
Painless betiğinin anlaşılması oldukça sezgiseldir; her belge için görüntüleme sayısını 1 oranında artırmak yeterlidir.
Elasticsearch'teki iç içe geçmiş nesneler, nesne dizilerinin tek bir ana belge içinde ayrı belgeler olarak indekslenmesine olanak tanıyan bir veri yapısıdır. İç içe geçmiş nesneler, nesnelerin içindeki nesneler gibi, doğal olarak iç içe geçmiş bir yapı oluşturan karmaşık verilerle uğraşırken kullanışlıdır. Tipik bir Elasticsearch belgesinde, nesne dizileri düzleştirilir ancak iç içe geçmiş veri türünün kullanılması, dizideki her nesnenin bağımsız olarak indekslenmesine ve sorgulanmasına olanak tanır.
Elasticsearch'te iç içe geçmiş nesneleri güncellemek için ağrısız komut dosyaları da kullanılabilir.
from elasticsearch import Elasticsearch # Connect to your Elasticsearch instance es = Elasticsearch([{'host': 'localhost', 'port': 9200}]) # Index name and document ID for the example index_name = 'your_index' document_id = 'your_document_id' # Specify the nested field and the updated value nested_field = "nested_field_name" updated_value = "new_value" # Define the Painless script for the update update_script = { "script": { "lang": "painless", "source": "ctx._source.nested_field_name = params.updated_value", "params": { "updated_value": updated_value } } } # Perform the update using the Update API and the Painless script try: es.update(index=index_name, id=document_id, body=update_script) print("Nested object updated successfully!") except Exception as e: print(f"Error updating nested object: {e}")
Elasticsearch'te bir belgeye yeni bir alan eklemek, bir indeks işlemiyle gerçekleştirilebilir.
Güncelleme API'sini kullanarak mevcut bir belgeyi yeni alanla kısmen güncelleyebilirsiniz. Dizinde dinamik eşleme etkinleştirildiğinde yeni bir alanın tanıtılması basittir. Bu alanı içeren bir belgeyi indekslemeniz yeterlidir; Elasticsearch otomatik olarak uygun eşlemeyi bulur ve yeni alanı eşlemeye ekler.
Dizindeki dinamik eşleme devre dışı bırakıldığında, güncelleme eşleme API'sini kullanmanız gerekecektir. Film dizinine bir “kategori” alanı ekleyerek dizin eşlemesinin nasıl güncelleneceğine ilişkin bir örneği aşağıda görebilirsiniz.
PUT /movies/_mapping { "properties": { "category": { "type": "keyword" } } }
Kod basit olsa da, veriler değişmez segmentlerde depolandığından, Elasticsearch bu güncellemeleri gerçekleştirmek için dahili olarak çok fazla ağır iş yapıyor. Sonuç olarak, Elasticsearch bir belgede kolayca yerinde güncelleme yapamaz. Güncelleme gerçekleştirmenin tek yolu, hangi API'nin kullanıldığına bakılmaksızın belgenin tamamını yeniden indekslemektir.
Elasticsearch, Apache Lucene'yi temel olarak kullanıyor. Lucene endeksi bir veya daha fazla bölümden oluşur. Segment, genel endeksin bir alt kümesini temsil eden, bağımsız, değişmez bir dizin yapısıdır. Belgeler eklendiğinde veya güncellendiğinde, yeni Lucene segmentleri oluşturulur ve eski belgeler geçici olarak silinmek üzere işaretlenir. Zamanla yeni belgeler eklendikçe veya mevcut belgeler güncellendikçe birden fazla segment birikebilir. Endeks yapısını optimize etmek için Lucene periyodik olarak daha küçük segmentleri daha büyük segmentlerle birleştirir.
Her güncelleme işlemi bir yeniden indeksleme işlemi olduğundan, tüm güncellemeler esasen geçici silmelerle yapılan eklemelerdir.
Bir güncellemeyi bir ekleme işlemi olarak ele almanın maliyet etkileri vardır. Bir yandan, verilerin geçici olarak silinmesi, eski verilerin bir süre daha tutulduğu ve endeksin depolama ve hafızasının şişirildiği anlamına gelir. Geçici silme, yeniden indeksleme ve çöp toplama işlemlerinin gerçekleştirilmesi de CPU'ya ağır bir yük getirir; bu yük, bu işlemlerin tüm kopyalarda tekrarlanmasıyla daha da kötüleşir.
Ürününüz büyüdükçe ve verileriniz zamanla değiştikçe güncellemeler daha karmaşık hale gelebilir. Elasticsearch'ün performansını korumak için kümenizdeki parçaları, analizörleri ve belirteçleri güncellemeniz gerekir; bu da kümenin tamamının yeniden indekslenmesini gerektirir. Üretim uygulamaları için bu, yeni bir küme kurulmasını ve tüm verilerin taşınmasını gerektirecektir. Kümelerin taşınması hem zaman alıcı hem de hataya açık bir işlem olduğundan hafife alınacak bir işlem değildir.
Elasticsearch'teki güncelleme işlemlerinin basitliği, sistemin altında gerçekleşen ağır operasyonel görevleri maskeleyebilir. Elasticsearch, her güncellemeyi bir ek olarak ele alır ve tüm belgenin yeniden oluşturulmasını ve yeniden indekslenmesini gerektirir. Sık güncelleme yapılan uygulamalar için bu, her dakika milyonlarca güncellemenin gerçekleştiği Netflix örneğinde gördüğümüz gibi hızla pahalı hale gelebilir. İş yükünüze gecikme katan toplu API kullanarak güncellemeleri toplu hale getirmenizi veya Elasticsearch'te sık güncellemelerle karşılaşıldığında alternatif çözümlere bakmanızı öneririz.
Bulutta yerleşik bir arama ve analiz veritabanı olan Rockset, Elasticsearch'e değiştirilebilir bir alternatiftir. Değiştirilebilirliği ile popüler hale gelen bir anahtar-değer deposu olan RocksDB üzerine inşa edilen Rockset, belgelerde yerinde güncellemeler yapabilir. Bu, belgenin tamamı yerine yalnızca bireysel alanların değerinin güncellenmesine ve yeniden dizine eklenmesine neden olur.
Güncelleme ağırlıklı iş yükleri için Elasticsearch ve Rockset'in performansını karşılaştırmak isterseniz, 300 $ krediyle Rockset'in ücretsiz deneme sürümünü başlatabilirsiniz.