Eşzamanlılık sorunlarını işlemek için en iyi yolu

11 Cevap php

i LAPP (linux, apache, php ve postgresql) ortamı var, ama soru Postgres ya da MySQL üzerinde de oldukça aynıdır.

(. Vb tahminleri, faturalar,) birçok şemalar (app kullanarak her müşteri için bir tane) 1 postgres DB yapılandırılmış bir cms müşteriler, belgeleri ele i geliştirilen app, ve diğer verileri, var; Etrafta 200 şemalar, 15 kişi (ort) tarafından aynı anda kullanılan her biri varsayalım.

EDIT: Her masada last_update adında bir zaman damgası alanı ve zaman damgası satır güncelleme her zaman güncelleştirme bir tetikleyici var.

Durum:

  1. İnsanlar Foo ve Bar, her belge detayları ile bir formu kullanarak, belgeyi 0001 düzenliyorsunuz.
  2. Foo örneğin, gönderi bilgilerinizi değiştirin.
  3. Telefon numaralarını değiştirmek Bar ve belgede bazı öğeler.
  4. Foo 'Kaydet' düğmesine basın, app db güncelleyin.
  5. Bar basın eski gönderi detayları ile formunu göndermeyi bar sonra 'Kaydet' düğmesini.
  6. Veritabanında, Foo değişiklikler kaybolmuş.

Olmasını istediğiniz durum:

  1. İnsanlar Foo, Bar, John, Mary, Paoul her belge detayları ile bir formu kullanarak, belgeyi 0001 düzenliyorsunuz.
  2. Foo örneğin, gönderi bilgilerinizi değiştirin.
  3. Bar ve diğerleri başka bir şey değiştirmek.
  4. Foo 'Kaydet' düğmesine basın, app db güncelleyin.
  5. Bar ve diğerleri bir uyarı 'Uyarı olsun! Bu belge, başkası tarafından değişimi t olmuştur. 'Fiili verileri yüklemek için buraya tıklayın.

Bunu yapmak için ajax kullanmak için merak ettik; sadece belge ve son güncellenen, timestamp kimliği ile bir gizli alanını kullanarak, her 5 saniyede başka, uyarı iletişim kutusunu göster, son güncellenen kez aynı olup olmadığını kontrol edin ve hiçbir şey yapmayın.

Yani, sayfa check-son-update.php gibi görünmelidir:

<?php
//[connect to db, postgres or mysql]
$documentId = isset($_POST['document-id']) ? $_POST['document-id'] : 0;
$lastUpdateTime = isset($_POST['last-update-time']) ? $_POST['last-update-time'] : 0;
//in the real life i sanitize the data and use prepared statements;
$qr = pg_query("
    SELECT
        last_update_time
    FROM
        documents
    WHERE
        id = '$documentId'
");
$ray = pg_fetch_assoc($qr);
if($ray['last_update_time'] > $lastUpdateTime){
    //someone else updated the document since i opened it!
    echo 'reload';
}else{
    echo 'ok';
}
?>

Ama db bir (veya daha fazla ...) belgeleri açtınız her kullanıcı için her 5 saniyede bir stres sevmiyorum.

Db nuking olmadan So, what can be another efficent çözüm?

Örneğin ben her bir belge için boş bir txt dosyası oluşturma, dosyaları kullanmak için düşündüm ve her belge, i 'dokunma' de 'son değiştirilme zamanı' güncelleme dosyası güncellendi ... ama bu daha yavaş olacağını tahmin i aynı belgeyi düzenleme çok kullanıcıya sahip olduğunda db ve daha büyük sorunları verir.

Başkası daha iyi bir fikir ya da herhangi bir öneriniz varsa, detaylı olarak tarif edin!

* - - - - - UPDATE - - - - - *

Ben kesinlikle sorgu oldukça hızlı olacak eğer dont check 'son güncelleme damgası' için db vurmak DEĞİL seçilmiş, (ana) veritabanı sunucusu, fullfill o şey onun aşırı artırmak için fikir gibi dont diğer görevleri vardır .

Yani, bu şekilde alma im:

  1. Every time a document is updated by someone, i must do something to sign the new timestamp outside the db environment, e.g. without asking the db. My ideas are:
    1. Dosya sistemi: her belge için ben her belge, i 'dokunma' dosyası güncellemesi, belgenin kimliği olarak adlandırılan bir empry txt dosyaları oluşturabilirsiniz. Bu boş binlerce dosya var bekleyen Im.
    2. APC, php cache: Bu muhtemelen ilk olandan daha esnek bir yol olacak, ama alışkanlık php yürütülmesine kendisi yavaşlatabilir, ya da sunucu belleği tüketmek apc kalıcı veri binlerce ve binlerce tutmak merak im. Bu yolu seçmek için korkuyor im biraz.
    3. Başka bir db, sqlite veya mysql (daha hızlı ve daha hafif basit db yapılarıyla olduğu) sadece belgeler kimliği ve damgaları depolamak için kullanılır.
  2. I (dosyaları, apc, alt-db) im ciddiye tüm bu .. uzun yoklama istekleri işlemek için, bir alt etki alanında başka bir web sunucusu (lighttp?) Kullanmayı düşünerek tercih ne olursa olsun yolu.

YET ANOTHER EDIT:

Dosyanın yolu çalışmak olmaz.

APC çözüm olabilir.

DB vurmak sadece mümkün olduğunca hızlı ve hafif olması gerekir (sadece iki sütun document_id ve last_update_timestamp ile) damgaları işlemek için bir tablo oluştururken, çok çözüm olabilir.

Uzun yoklama: statik dosyaları (. Görüntüleri, css, js, vs) yüklemek için apache altında lighttpd kullanarak, ben seçeceğim yolu ve sadece uzun yoklama türü için; Bu özel yoklama için, apache2 yükünü hafifletecek.

Apache Proxy-up lighttpd tüm bu isteği olacaktır.

Şimdi, ben sadece db çözeltisi ve APC çözüm arasında karar vermek zorunda ..

ps: beni zaten yanıtladı kime tüm sayesinde, gerçekten da faydalı olmuştur!

11 Cevap

Ben muhtemelen bunun için veritabanı vurmak olmaz katılıyorum. Ben bu bilgiyi korumak için APC önbelleği (ya da diğer bazı bellek önbelleği) kullanmak herhalde. Ne açıklayan ayrıntılı rekor düzeyde açıkça iyimser kilitleme olduğunu. Seviyesi yüksek veritabanı yapısı daha az ile uğraşmak gerekir. Eğer bir yapı içinde birden fazla tablo ile kontrol etmek istediğiniz gibi geliyor.

Ben kimlikleri (APC) bir önbellek ve tablo adıyla anahtarlı son güncelleme zaman damgaları korumak olacaktır. Yani, örneğin ben her giriş kimliği ile anahtarlı ve gerçek değeri son güncelleme damgası olduğunu tablo adlarının bir dizi olabilir. Orada dizileri veya diğer yapılar bu kurmak için muhtemelen birçok yolu vardır ama fikir olsun. Önbellek girdileri belli bir süre sonra kaldırılır böylece muhtemelen önbellek için bir zaman aşımı eklemek istiyorum - yani ben) önbellek büyümeye ve 1 gün eski kayıtlar artık yararlı olmadığını varsayalım istemem.

Bu mimari ile (APC kurma ek olarak) aşağıdakileri yapmanız gerekir:

  • Herhangi bir (geçerli) bir tablodaki herhangi bir güncelleme üzerinde, zaman damgası ile yeni APC önbellek girdisini güncelleyin.

  • ajax içinde sadece "back" yerine "back" veritabanına tüm yol daha php (giriş kontrol etmek için APC önbelleği elde etmek için) kadar gitmek.

I = NEREYE kimliği gibi UPDATE deyiminde bir durum kullanabilirsiniz düşünüyorum? VE last_update =?.

Fikir o satır okuma sonuncusu olduğunda sadece güncelleme başarılı olacaktır. Başkası bir şey taahhüt varsa, başarısız olur, ve siz başarısız olduğunuz öğrendikten sonra, size değişiklikleri sorgulayabilirsiniz.

Her kayıt için sürüm damgası alan bazı tip gerekir. Nedir sürece bir kayda herhangi bir değişiklik yapmadan bu sürüm damgası farklı olmanın neden olacağı garanti gibi önemli değildir. En iyi uygulama daha sonra kontrol ve yüklenen kaydın sürüm damgası kullanıcı tıkladığında kaydetmek DB sürüm damgası olarak aynı olduğundan emin olmak için, ve farklı kolu ise o.

Eğer size kalmış nasıl ele. Eğer DB yeniden sunmak isterdim en azından böylece kullanıcı hala kaydetmek istediğiniz olduğunu doğrulayın. Bundan bir yukarı yeni DB kayıt içine onların değişiklikleri birleştirmek ve daha sonra birleştirme doğru çalıştı doğrulamak için onlara sormak girişimi olacaktır.

Periyodik yoklamak istiyorsan sistem taşıma kapasitesine sahip herhangi bir DB anket yük almak gerekir. Her 5 saniyede bir 10 kullanıcı yoklama saniyede 2 işlemler olduğunu. Bu önemsiz bir yük olduğunu ve hiçbir sorun olmalıdır. Sadece (örneğin her 4-6 saniyede bir, yapmak, yerine tam olarak her 5 saniyede bir yapıyor) biraz yoklama zaman titreme, gerçek yüke yakın ortalama yükü tutmak için.

Donnie cevabı (yoklama) muhtemelen en iyi seçenek - basit ve eserleri. Neredeyse her durumda (onun olası basit PK arama bile çok popüler bir sitede, performans zararı) ele alacağız.

, Oylamayı önlemek istiyorsa bütünlüğü için, ve, bir push-model kullanabilirsiniz. Wikipedia makalede anlatılan çeşitli yolları var. Eğer aracılığıyla yazma önbelleğini (Eğer kayıt güncelleme, her şey sen önbelleğini güncelleyin) koruyabilirsiniz, o zaman neredeyse tamamen veritabanı yükünü ortadan kaldırabilir.

Olsa, bir zaman damgası "LAST_UPDATED" sütunu kullanmayın. Aynı saniye içerisinde Düzenlemeleri duyulmamış değildir. Iki istekleri aynı sunucuya, aynı saniyede geldi eğer sağlamak için ekstra bilgi (güncelleştirme, uzak adres, liman, vb yaptım server) eklerseniz onunla uzak alabilir, size fark tespit olabilir. O hassasiyet ihtiyacınız varsa, olsa, siz de (mutlaka o kaydın ömrü içinde sadece benzersiz, artan bir tamsayı olmak zorunda değildir) benzersiz bir revizyon alanı kullanabilirsiniz.

Biri kalıcı bağlantılarını sözü - bu (her bağlantı doğal olarak, veritabanı ve ana makinede kaynaklarını tüketir) yoklama sorgularının kurulum maliyetini azaltacaktır. Tek bir bağlantı (ya da mümkün olduğunca az) (mümkün olduğunca uzun ya) her zaman açık tutmak ve (istenirse, önbelleğe alma ve memoization ile birlikte) bu kullanmak istiyorsunuz.

Son olarak, UPDATE veya INSERT üzerinde bir koşul eklemek için izin SQL ifadeler vardır. Benim SQL gerçekten paslanma, ama ben UPDATE ... WHERE ... gibi onun şey düşünüyorum. Bu düzeyde koruma maç için, önce güncelleştirme (ve hata işleme ve entail temizleme tümü) göndermek için kilitleme kendi satır yapmak gerekir. Onun olası bu gerekiyordu; Ben sadece tamlık için söz ediyorum.

Edit:

Sizin çözüm ince (cache damgaları, bir başka sunucuya vekil yoklama istekleri) duyulur. Ben yapmak istiyorum tek değişiklik, her tasarrufu üzerinde önbelleğe damgaları güncellemek için olduğunu. Bu önbellek taze tutacaktır. Önlemek kaydederken ben de db doğrudan damgası kontrol ediyorum bir nedeni bayat önbellek verilerine sinsi kaydedin.

Önbelleğe alma için APC kullanırsanız, daha sonra ikinci bir HTTP sunucusu mantıklı değil - Eğer aynı makinede (APC paylaşılan hafıza kullanır) çalıştırmak gerekiyor. Aynı fiziksel makine ama ikinci bir HTTP sunucusu ek yük ile, işi olacaktır. Kapalı (sizin durumunuzda, lighttpd'nin) ikinci bir sunucuya yoklama istekleri yüklemek istiyorsanız, o zaman ikinci bir fiziksel makine üzerinde Apache önünde kurulum lightttpd için daha iyi olacak ve paylaşılan bir önbelleğe alma sunucusunu (memcache) kullanmak istiyorsunuz ki lighttpd sunucu önbelleğe damgalarını okuyabilir ve Apache önbelleğe damgaları güncelleyebilirsiniz. Çoğu istekleri ağır-ağırlık Apache süreç kullanımını önlemek için yoklama istekleri, eğer Apache önünde lighttpd koymak için gerekçe vardır.

Muhtemelen, gerçekten hiç ikinci bir sunucu gerekmez. Apache ek istekleri işlemek gerekir. Bunu yapamazsınız, o zaman ben (işçi onlar öldürülmeden önce işlemek için izin kaç istekleri çalıştırmak ve süreçleri kaç denetlemek özellikle direktiflerini) yapılandırmanızı tekrar ediyorum.

Veritabanı sorgulama Sizin yaklaşım en iyi biridir. Bunu her 5 saniyede bir yapmak ve 15 eşzamanlı kullanıcı varsa o zaman 3 ~ bakıyoruz bir saniye sorgular. Bu verilerin sadece bir satır döndüren, çok çok küçük bir sorgu olmalıdır. Veritabanı 3 İşlemleri ikinci idare edemiyorlar, o zaman 3 sorgular / saniye bir şey değildir çünkü iyi bir veritabanına bakmak zorunda kalabilirsiniz.

Şey her alanı diff zorunda kalmadan değişti eğer hızlı bir şekilde görebilirsiniz böylece tablodaki kayıtları zaman damgası.

Hazırda yapmak için bir sürüm alanı kullanır. Her tablo, böyle bir alan vermek ve her güncellemede bunu artırmak için bir tetikleyici kullanabilirsiniz. Bir güncelleme saklarken, veriler, önceki okundu sürümü ile güncel sürümünü karşılaştırın. Bu uyuşmuyorsa, bir istisna atar. Check-ve-güncelleme atom yapmak için işlemleri kullanın.

Bu biraz konu dışı, ama size bir çarpışma olsun ne zaman xdiff iyi bir kullanıcı rehberlik geri göndermek için (hangi unutmak, ya da PECL paketi) PEAR paketi kullanabilirsiniz.

Sadece veritabanına yazarken üzerinde değişti alanları güncellemek Öncelikle, bu veritabanı yükünü azaltacaktır.

İkinci olarak, bir eski zaman damgası varsa o veritabanındaki güncel sürümü daha sonra müşteriye uyarı atmak, son güncellemenin damgası sorgulayabilirsiniz.

Üçüncü nedense eşzamanlı iki yönlü bir bağlantı sağlayan, sunucu ile kalıcı bağlantı çeşit olsa da, müşteri için bu bilgileri bas etmektir.

Polling is rarely a nice solution.
You could do the timstamp check only when the user (with the open document) is doing something active with the document like scrolling, moving the mouse over it or starts to edit. Then the user gets an alert if the document has been changed.

.....
I know it was not what you asked for but ... why not a edit-singleton?
The singleton could be a userID column in the document-table.
If a user wants to edit the document, the document is locked for edit by other users.

Veya bilgi tek tek alanların / gruplar üzerindeki düzenleme-singletons var.

Sadece bir kullanıcı bir seferde belgeyi düzenleyebilirsiniz. Başka bir kullanıcı belgesi vardır ve tek bir zaman damgası çek düzenlemek istiyorsanız belge değiştirilmiş ve yeniden ortaya koymaktadır.

Bir singleton ile kullanıcı "dokunuşlar" ve / veya belge düzenlemek istiyor hiçbir yoklama ve sadece bir zaman damgası onay var.

But perhaps a singleton mechanism doesn't fit your system.

Regards
   Sigersted

Ahhh, easyer oldu i bile.

Yani, noktayı sağlar: i genel bir veritabanı (pgsql veya mysql farketmez) var, birçok genel nesneler içerir.

Ben $ x var (aslında $ x = 200, ama yakında 1000 ulaşmayı umuyor, büyüyor) bu veritabanının tam kopya, ve bunların her biri için günde 9 saat 20 (ort 10) kullanıcıları için.

Bu kullanıcılardan biri kaydını, herhangi bir kaydı inceleyen varsa birisi aynı kaydı düzenlemek eğer, ben onu tavsiye gerekir.

Foo geri geldiğinizde en bir kahve içmek için oturmak, Foo belgeyi 0001 izliyor diyelim, Bar, açık ve aynı belge düzenlemek, o başka bir 'Dikkat, birisi düzenlenmiş bu belge görmek gerekir! tha sayfayı yenilemek için buraya tıklayın. '.

I atm gerek That'all, muhtemelen değişiklik ve geri alma görmek için bir yol ekleyerek, bu durumu uzatmak edeceğiz, ama bu nokta değildir.

Bazılarınız foo belgeyi kaydetmeye çalıştığınızda yalnızca 'son güncelleme' damgası kontrol etmek için önerdi; De bir çözüm olabilir, ama gerçek-zamanlı (10 sn Deelay) şeylere ihtiyacı olabilir.

Uzun yoklama, kötü bir yol, ama sadece biri olarak görünüyor.

Peki, ne yaptık:

  1. Yüklü benim makinede Lighttp (ve fastcgi olarak php5);
  2. Loaded Apache2 proxy modülü (tüm veya 403 hata vuracaktır);
  3. 81'e (bu apache2 tarafından kullanılır) 80 lighttpd'nin noktasını değişti;
  4. Polling.mydomain.com için mydomain.com / yoklama / * dan isteği vekalet üzere yapılandırılmış apache2 (Lighttp ile servis)
  5. Şimdi, ben Apache2'yi yükünü azaltmak için, ben statik içerik (resim, vb.) Yoklama için hem kullanımı ve yük olacak başka bir alt-http hizmeti var.
  6. Becose i dont want to nuke the database for the timestamp check, i've tryed some caches system (that can be called from php).
    1. APC: oldukça basit yüklemek ve çok hafif ve daha hızlı, yönetmek, bu sadece önbellek iki cgi süreci arasında paylaşılabilir olurdu (i önbellek Apache2'yi en php sürecinin bir değer saklamak gerekir .. benim ilk tercihi olmak ve onu okurdu Lighttpd en php sürecinden)
    2. APC göre yaklaşık 4-5 kat daha yavaş, ama benim bir ortamda her yerde dokundu edilebilir tek bir süreç olarak çalıştırın: Memcached. I'll go with this one, atm. (bile yavaş, ben bunu yapacağım kullanımı oldukça basittir).

Şimdi, ben sadece ho 'baskı altında' hareket ve optimize edecek görmek için bazı test verileri yüklerken bu sistemi denemek zorunda.

Ben (chat?) Bu ortamı diğer uzun yoklama durumlar için çalışacak suppost

Bana verdiğin herkese teşekkürler duymak!

Ben öneririm: İlk değişmiş olabilir kaydını sorguladığınızda, bir yerel kopya üzerine asın. Ne zaman "güncelleme" Kopyanızda karşı kilitli tablo / satırda kopyasını karşılaştırmak ve eğer değişti, geri kullanıcıya tekme.