Arkadaşlar diyelim ki 9gag gibi bir sitemiz var. Tamamiyle varsayımsal konuşuyorum. Minimum arama aralığı 1 Gün olacak şekilde. Dün en çok tıklanan içerikler, Bu hafta en çok tıklanan içerikler, Bu ay en çok tıklanan içerikler, Seçilebilir bir tarih aralığında en çok tıklanan içerikler Listelemek için sistem kurmak istediğimizi düşünelim. Bunu en optimum nasıl yapabiliriz? Benim aklıma iki tane çzüm geliyor. 1-)Bir tablo yaparız. Kullanıcı içeriğe tıkladığında, tıklanılan içeriğinID sini gsteren ve O günün tarihi olan satır varsa, o satırın tıklamaSayısı'nı bir arttırız. Yoksa o satırı içerikid ve ogununTarihi ve TıklamaSayisi=1, şeklinde oluştururuz. Tablo Yapısı: ID, içerikID, OgununTarihi, TıklamaSayısı OgununTarihi indexed içerikID foreignKey ID primaryKey Auto İnc. Sonrası malum zaten. Mesela dün en çok tıklananlar derse SELECT ID FROM Tiklamalar WHERE OgununTarihi = Dün ORDER BY TıklanmaSayisi Geçen Ay dersede SUM Tiklanma Sayilarını SUM ile toplayacağız, OgununTarihini de BETWEEN ile günleri belirleyeceğiz ve group by tiklanmaSayisi le gstereceğiz . 2-)Kişiler her içeriğe tıkladığında tıklamalarını aşağıdaki şekildeki bir tabloya satır olarak ekleriz. Tablo Yapısı: ID, icerikID, TiklamaZamani , tiklayanIP, userAgentBilgisi, TiklayanUserId, tiklamaSayisi=1 Gene aynı şekilde between ile tarih aralığını belirler, group by ile icerikID yi belirler, Sum ilede en çok tiklananları buluruz gibime geliyor. İkinci yntemin avantajları günlük değil saatlik hatta dakikalık istatistikleri bile çıkarabiliriz. İkinci yntemin dezavantajı performas kaybı çok daha fazla olur ve veritabanında çok şişme yapacağı aşikar. Birinci Yntem daha optimum gibi. Düşüncelerinizi bekliyorum arkadaşlar. Daha optimum çzüm neriniz var mı? SQL harici çzümlerde olur. NOsql, Neo4J veya başka bir çzüm nerinizde olabilir. Ama ncelikli olarak SQL tabanlı bir RDBMS ile bu olayı nasıl sağlayabiliriz? Bu tür konulara pek ilgili olmuyor umarım kafa patlatanlar çıkar. Bu imzayı her grdüğünüzde 4gb kotanızın 0,00000358559191226959228515625 azalmaktadır. Bilerek ve istenerek bu imza yapılmıştır. Amaç kotanızı smürmektir

Ziyaretçilerin de istatistiklerini tutmak hoş bir ayrıntı ancak suistimale açık bir ayrıntı. Kıl bir eleman her clickte ua değiştiren proxy destekli bir clicker dayarsa siteye o 1k row olur 100k row. Tek bir hücrede sadece hit tutmak daha iyidir ziyaretçiler için ya da gerekli nlemleri almak lazım. Her gün 1k row gelse diye düşünürsek 2. gün 2k rowda count veya sum yapmak yerine parça tablolardaki 1k rowda işlem yapmak daha avantajlıdır bu nedenle 1. mantıktaki olayı tablolara blüp desteklemek daha mantıklı geliyor bana. nosql tarafında hiç deneyimim yok o konulara giremiyorum ancak yüzeysel olarak ele alırsam olay sadece performanssa eğer istatistikleri tutacak veritabanını kullanıcılarla anlık etkileşime sokmak yerine belirli saatlerde cronjob ile nosql tarafında tuttuğun verileri alıp arka planda işleyerek dinamik değil statik içerik bastırabilirsin işlediğin verilere gre. + ek yukardaki murid bahsetmiş cassandra bu konularda iyiymiş şurda bahsedilmiş http://stackoverflow.com/questions/3515114/what-is-the-best-database-storage-to-store-statistic-data pesimistzombie tarafından 23/Eki/14 02:44 tarihinde düzenlenmiştir :|

basit bir siteyi ele alırsak ilk tercih mantıklı olan zaten ancak çok çok fazla veri kaydı tutulacaksa bu tür işlemler için partitioning olayını neriyorlar benim hiç bu tür projem olmadığı için mantığını tam olarak bilemiyorum; http://dev.mysql.com/doc/refman/5.1/en/partitioning-overview.html Türkçe: http://blog.mustafakirimli.com/mysql-partition-kullanimi-ve-partition-turleri/328 http://www.mssqltips.com/sqlservertip/2780/archiving-sql-server-data-using-partitioning/ Türkçe: http://www.yazilimdevi.com/Makaleler-669-sql-server-table-partitioning-and-index-partitioning.aspx Ancak basitçe şyle bir şey de nerebilirim; birden fazla tablo oluşturman sıkıntı olmuyorsa ve veriyi sadece 1 ay saklayıp eski verileri yavaş yavaş alttan sildireceksen günlük | haftalık | aylık tablolarda verileri saklamak o günün denk geldiği tabloda işlem yaptırmak biraz daha performanslı olacaktır bence. Partitioning sql aramalarında çok fayda sağlıyor yeterki partition yapılan field üzerinden aramaları yap. Onu hertürlü yaparım hocam onda sıkıntı yok. Ancak dediğim gibi bunun için ekstra ve optimum bir saklama yntemi var mı acaba. Şimdi şyle bir gz geçirelim. 1. Yntemde 300K içerik olan ve günde 20K hit olan siteden hergün farklı şekilde 1K içerik'e tıklandığını düşünürsek. Hergün en az 1K row eklenecek veritabanına. 2. Yntem zaten bu konuda çok şişme yapacağı kesin. 20K hitin 20K row eklenmesine sebeb olacak. Şişmenin alası. Senin belirttiğin yntemde AYRICA her ay ve her hafta 1K row daha eklenecek yani ayda toplam 4K+1K = 5K fazladan ROW. Ama her ay geçen ayın günlük HIT verilerini silersem (Bu da 30 1K dan 30K satır silmiş olacağım) çok rahatlayacağım kesin. fakat bu seferde geçmiş zamanların günlük hatta bir kaç günlük istatistiki bilgilerine erişemeyeceğim. Esnekliğim azalacak. Ya aylık ya da günlük verilerine erişebileceğim. Şimdi byle bir istatistik bilgi aslında aynı veritabanında tuttmakta büyük hata olur. Muhtemelen ayrı bir veritabanda tutacağım. Acaba NOsql veya diğer veritabanı türlerinin bu tür istatistiki bilgileri gerek saklarken gerek işlemede faydası olur mu? Olursa ne kadar olur. İstatistiki bilgilerin karar destek sistemlerinden başka bir yerde kullanılacağını pek düşünmem. Bu belirttiğim sistem bir 9gag değilde büyük bir e-ticaret sistemi olsaydı . Bu verilerin nemi ileride çok fazla olacağı kesindi. Byle büyük sistemlerde bu verilerin uçmaması çok nemli ancak benim bahsettiğim gibi 9gag gibi bir sistemde uçsada olur açıkçası pek nemli değil. O yüzden illa SQL tabanlı veri RDBMS de saklamakta gerekmiyor aslında. DrKill tarafından 23/Eki/14 02:10 tarihinde düzenlenmiştir Bu imzayı her grdüğünüzde 4gb kotanızın 0,00000358559191226959228515625 azalmaktadır. Bilerek ve istenerek bu imza yapılmıştır. Amaç kotanızı smürmektir

hocam soruyu ve yorumları çok dikkatli okuyamadım, baya yorgunum anlamak zor geldi :) ama anladığım kadarıyla hızlı bir çzüm arıyorsun bunun için nosql yapısını kullanabilirsin. nosql çzümleri ilişkisel değildir o yüzden sistemin bir parçasını orada tutabilirsin. yani şyle anlatayım.. veritabanın şuan neyse aynen onu kullan ama diğer istatisik ya da her neyse için gerekli bilgileri nosql çzümlerinde tutman işini kolaylaştırır hız açısından.. soruda belirttiğin tablo yapısnı nosql de yapabilirsin. orada ne kadar veri olduğu çok nemli değil.. nosql çzümleri CAP felsefesine gre çalışıyor. Tabi aynı anda en fazla ikisi seçilmeli http://en. wikipedia. org/wiki/CAP_theorem burda cap ile ilgili bilgi alabilirsin.. hangisi işine yarayacak senin? mesela AP uygun grünüyor senin için.. yani her zaman belirli bir değer dnderecek bir sistem , bunun için kullanabileceğin cassandra, couchdb, simpledb, amazon dynamo ve linkedln voldermort. Ben cassandra yı çok sevdim :) c# da cassandra ile ilgili yazdığım bir projenin kodlarını paylaşayım biraz fikir verir. Cassandra için CQL dilini kullanacaksın. SQL a çok benziyor.. using System; using System. Collections. Generic; using System. Diagnostics; using System. Linq; using System. Text; using System. Threading; using System. Windows. Forms; using Cassandra; using Cassandra. Data. Linq; using Cassandraemon; namespace CassandraAssignment { public class CassandraHelper { private Cluster cluster; private Session session = null; private Dictionary<string, movieHolder> actorList; private HashSet<string> genreList; private List<Movie> movieList; public int startedYear; public int endedYear; private int processCounter = 0; private object lockedObj; public delegate void Completed ; public event Completed OnCompleted; protected virtual void OnOnCompleted #region Constructor public CassandraHelper #endregion #region Connection Function public bool Connect { try catch return true; } #endregion #region Create KeySpace If Not Exists and use group16 public void createKeySpaceIfNotExists { session. Execute("CREATE KEYSPACE IF NOT EXISTS group16 WITH REPLICATION = ;"); session. Execute("Use group16"); } #endregion #region Create Tables public void createTables { ExecuteQuery("CREATE TABLE IF NOT EXISTS Movies ;"); //title, year, rating, genres, actors // ExecuteQuery("CREATE TABLE IF NOT EXISTS Movies "); //CREATE TABLE actors ;") ExecuteQuery("CREATE TABLE IF NOT EXISTS Actors ;"); ExecuteQuery("CREATE TABLE IF NOT EXISTS ActorMovies (constantId int, filmCount int, actorName varchar, PRIMARY KEY ;"); // ExecuteQuery("CREATE TABLE IF NOT EXISTS Genres "); ExecuteQuery("CREATE TABLE IF NOT EXISTS GenreMovies (genreName varchar, rating float, movieTitle varchar, PRIMARY KEY ;"); ExecuteQuery("CREATE TABLE IF NOT EXISTS GenreList ;"); if (tablesAreEmpty else OnOnCompleted ; } #endregion #region If Tables Are empty? private bool tablesAreEmpty { var rowCountStr = ExecuteQuery("Select count from Movies"). GetRows . FirstOrDefault ; if return true; } #endregion #region insertMovicesFromTextDb (reads movies from text db of imdb private void insertMovicesFromTextDb #endregion private void increaseProcessCounter { lock { processCounter++; if cqlCompleted ; else if } } #region utils_MovieCompleted Event private void utils_MovieCompleted { movieList = movies; MessageBox. Show ; // foreach // / BEGIN BATCH INSERT INTO users VALUES ('user2', 'ch@ngem3b', 'second user') UPDATE users SET password = 'ps22dhds' WHERE userID = 'user2' INSERT INTO users VALUES ('user3', 'ch@ngem3c') DELETE name FROM users WHERE userID = 'user2' INSERT INTO users VALUES ('user4', 'ch@ngem3c', 'Andrew') APPLY BATCH; / } #endregion string movieCql = ""; private string actorCql = ""; private string genreCql = ""; private int movieCounter = 0; object objlock = new object ; #region Creating Cql Code for each movie to insert keyspace private void createCql { lock { var movie = m; movie. Title = movie. Title. Replace ; var actors = ""; if (movie. Actors. Count > 0) { for (int i = 0; i < movie. Actors. Count; i++) actors = actors. Remove(actors. Length - 1, 1). Trim . Replace ; } var info = String. Format("Year: , Rating: , Genres: , Actors: ", movie. Year, movie. Rank. ToString . Replace , seperateString , actors); movieCql += String. Format("INSERT INTO movies VALUES(' ',' ');\n", movie. Title, info); // movieCql += String. Format("INSERT INTO movies VALUES(' ', , ,' ',' ');\n", movie. Title, // movie. Year, movie. Rank. ToString . Replace , seperateString , seperateString ; // ExecuteQuery ; foreach { var actor2 = actor. Replace ; increaseActorFilmCount(actor2); / actorCql += String. Format("INSERT INTO ActorMovies VALUES( ,' ',' ');\n", counter, actor. ActorName, movie. Title); counter++; / } foreach { genreCql += String. Format("INSERT INTO GenreMovies VALUES(' ', ,' ');\n", genre, movie. Rank. ToString , movie. Title); addGenre ; } // movieCounter++; increaseProcessCounter ; // cqlCompleted ; } } #endregion string seperateString { // var arrtmp = list. Split ; var result = ""; foreach { if } if result = result. Remove(result. Length - 3, 3). Replace ;; return result; } #region Cql Completed function, if all cql codes are ready, execute query private void cqlCompleted { // lock // { // if // { MessageBox. Show ; string query=""; query = String. Format("BEGIN BATCH\n \nAPPLY BATCH;", movieCql); ExecuteQuery ; actorCql = ""; foreach { actorCql += String. Format("INSERT INTO ActorMovies VALUES( , ,' ');\n", 1, actor. Value. i, actor. Key); actorCql += String. Format("INSERT INTO Actors VALUES(' ', );\n", actor. Key, actor. Value. i); } query = String. Format("BEGIN BATCH\n \nAPPLY BATCH;", actorCql); ExecuteQuery ; // insert to genremovie table query = String. Format("BEGIN BATCH\n \nAPPLY BATCH;", genreCql); ExecuteQuery ; genreCql = ""; //insert genreList to genreList table foreach { genreCql +=String. Format("INSERT INTO GenreList VALUES(' ');\n", genre); } query = String. Format("BEGIN BATCH\n \nAPPLY BATCH;", genreCql); ExecuteQuery ; MessageBox. Show ; OnOnCompleted ; //add genre list // var frmMain = Application. OpenForms ; // frmMain. GetGenres ; // } // } } #endregion #region Execution Function that returns rowset public RowSet ExecuteQuery #endregion #region increase Actor filmed count. void increaseActorFilmCount { if (actorList. ContainsKey else } #endregion #region add Genres void addGenre { // if (actorList. ContainsKey // // else // } #endregion #region Get Genre List public List<string> GetGenres { var list= ExecuteQuery . GetRows ; var genreList = new List<string> ; foreach return genreList; } #endregion #region Get Top 10 Actors public RowSet GetTopActors #endregion #region GetTopMovies public RowSet GetTopMovies #endregion #region GetActorFilm Count public int GetActorFilmCount #endregion #region unused!! delete actor from actorMovie / public void deleteActor public void AddActor { var actorCql = String. Format("INSERT INTO ActorMovies VALUES( , ,' ');", 1, actorFilmCount, actorName); ExecuteQuery ; } / #endregion } class movieHolder { public movieHolder public int i; } } Ülkesini Seven Her Türk Vatandasi, Ülkesinin Sessiz Istilasi'na karsi durmak zorunda.

kusura bakma baya kod uzunmuş :S eğer cassandra yı kullanacaksan zaman buldukça yardımcı olabilrim Ülkesini Seven Her Türk Vatandasi, Ülkesinin Sessiz Istilasi'na karsi durmak zorunda.

basit bir siteyi ele alırsak ilk tercih mantıklı olan zaten ancak çok çok fazla veri kaydı tutulacaksa bu tür işlemler için partitioning olayını neriyorlar benim hiç bu tür projem olmadığı için mantığını tam olarak bilemiyorum; http://dev.mysql.com/doc/refman/5.1/en/partitioning-overview.html Türkçe: http://blog.mustafakirimli.com/mysql-partition-kullanimi-ve-partition-turleri/328 http://www.mssqltips.com/sqlservertip/2780/archiving-sql-server-data-using-partitioning/ Türkçe: http://www.yazilimdevi.com/Makaleler-669-sql-server-table-partitioning-and-index-partitioning.aspx Ancak basitçe şyle bir şey de nerebilirim; birden fazla tablo oluşturman sıkıntı olmuyorsa ve veriyi sadece 1 ay saklayıp eski verileri yavaş yavaş alttan sildireceksen günlük | haftalık | aylık tablolarda verileri saklamak o günün denk geldiği tabloda işlem yaptırmak biraz daha performanslı olacaktır bence. pesimistzombie tarafından 23/Eki/14 01:56 tarihinde düzenlenmiştir :|