*Disclaimer: ini semua dilihat dari kacamata Aplikasi (Developer/Architect), dan bukan dari kacamata Data (Database Administrator).
Di tempat saya consulting, lagi ada perdebatan seru nih: database di customer sudah mencapai lebih dari 1 Terabytes, dan performance aplikasi menjadi lambat. Solusi temporary adalah archiving old data tiap kali database mencapai lebih dari 1 Terabytes, dan voila, aplikasi kembali menjadi cepat.
Dan ini yg terjadi: Microsoft “nyalahin” ISV karena dibilang aplikasinya nggak bener bikinnya. Sebaliknya, ISV “nyalahin” SQL Server karena nggak bisa scalable untuk data diatas 1 Terabytes… nah loh, berantem aja tuh terus-terusan, not good in the long-term.
Untungnya sebagai konsultan, kita tidak perlu membela satu pihak. Cukup menanalisa, dan memberikan opini :)
Berikut konversasi saya (setelah di-edit agar lebih enak dibaca di blog dan menghapus reference ke pihak-pihak yg tak berdosa):
Pak, Masalah Terbesar ada di Hard Disk
Mohon lihat diagram berikut:
Rt = Response Time
Tx = Number of Transactions
GBs = Database Size
So grafik ini cukup menjelaskan kalo semakin besar database size dan semakin banyak jumlah transaksi, mau nggak mau response time pasti akan degradasi.
Prosesor semakin cepat.
Memory pun semakin besar, cepat, dan murah (DDR2, DDR3).
Network semakin cepat (Gigabit, Fiber).
Hard disk semakin besar, murah… tapi tetap aja 7,200 rotasi per menit (rpm) atau 10,000 rpm atau 15,000 rpm.
Intinya: Hard Disk adalah Elemen Terlambat dalam sebuah Sistem Komputer.
Distributed Hash Table (DHT)
Programmer suka sekali dengan hashtable, karena untuk mendapatkan objek Order dengan Id 001, cukup menggunakan kode:
Order o = OrderRepository.GetById(001);
// implementasi OrderRepository
public Order GetById (int id) {
return OrderHashtable[id];
}
Tapi pada kenyataanya, implementasi akan menjadi seperti diagram ini:
Problem muncul jika App Cache dan SQL Cache selalu “Cache Miss” alias persentase data yg di-request selalu tidak ada di Cache.
Andaikan database saya 8 GB, maka dengan diagram diatas, best-case nya selalu ada 50% Cache Miss, karena Total RAM Cache adalah 4 GB (2GB dari App dan 2 GB dari SQL). Performance akan mulai kerasa drop terlebih jika aplikasi membutuhkan banyak table joins.
Distributed Hash Table adalah konsep menggunakan memory idle di beberapa PC untuk menyimpan cache, dan menarik isi cache tersebut via network, dikarenakan kombinasi Gigabit Ethernet + Memory sekarang lebih cepat daripada hard disk memutar.
Katakanlah di kantor customer terdapat 8 PC yg kebanyakan digunakan hanya untuk Microsoft Office dan Internet Explorer saja. Maka PC-PC ini bisa dijadikan cluster untuk distributed hash table:
Dengan database size 8 GB tadi, sekarang Cache Hit (lawannya cache miss) saya bisa naik menjadi 99% . Ketika semua pegawai sudah pulang kantor, barulah data yg ada di DHT cluster di-synchron dengan SQL Server… jadinya hard disk SQL Server berputar untuk menyimpan writes ketika tidak ada users, dan hard disk SQL Server tidak pernah berputar untuk reads karena semua 8 GB data ada di DHT.
Nah untuk implementasi Distributed Hash Table ini, ada beberapa produk:
- Memcached: popular (digunakan Facebook, Flickr, dsb.) dan opensource.
- NMemcached: .NET-based Memcached buatan si Ayende (nHibernate guy).
- Cacheman: .NET-based Memcached buatan pegawai Microsoft (tapi bukan official MS product).
- SharedCache: .NET-based Memcached yg sepertinya powerful.
- Project Velocity: Microsoft Official Distributed Hash Table dan jawaban untuk Memcached (tapi masih beta bo’).
Database Sharding
Distributed Hash Table lumayan untuk bikin aplikasi scalable… selama Anda selalu bisa menambahkan mesin ke dalam Cache Cluster ketika data semakin bertambah..
Tapi jika tidak, maka masalah selanjutnya adalah membuat Database lebih Scalable… sebelum ada miskomunikasi, maksud saya Scalable disini adalah Performance akan tetap Stabil ketika Volume Data Bertambah. Jadi bukan high-availability (dimana saya pengen data selalu ada dan up 99%).
1) Pisahkan Reads dari Writes (Master-Slave)
Tembak Master Database ketika melakukan Insert, Update, Delete (Writes).
Replicate Master Database ke Slave.
Tembak Slave Database ketika melakukan Select (Reads).
2) Pisahkan Table (Vertical Partitioning)
Jika setelah implementasi Master-Slave ternyata performance masih dirasa kurang, maka pemisahan Table bisa dilakukan:
3) Pisahkan Rows (Sharding)
Jika setelah Vertical Partitioning juga masih dirasa lambat, maka opsi terakhir adalah Sharding:
Opsi 1) dan 2) bisa menggunakan SQL Server Replication. Opsi Sharding ini bukan Replication, karena Shard 1 datanya memang berbeda dengan Shard 2.
Sharding pada intinya adalah memecah 1 Database besar menjadi beberapa database kecil. Argumen untuk sharding adalah karena database yg kecil akan memberikan performance yg sangat cepat.
Maksud diagram diatas adalah penggunaan CustomerId dan Sistem Modulo 5 untuk menentukan di Shard mana data Customers dan Orders akan ditaruh.
Contoh: CustomerId = 1001 --> 1001 modulo 5 = 1 , maka data Customer ini akan ditaruh di database shard #2.
Codingnya pun harus ganti total, karena dibutuhkan argumen CustomerId untuk menentukan Shard-nya:
Customer c = CustomerRepository.GetById(1001);
// implementasi CustomerRepository
public Customer GetById (int cid) {
// Check Memcached
var memcache = new Memcache();
var customer = memcache[cid];
if (customer == null) {
// Cache miss, harus nembak database
var shardId = cid % 5;
IDbConnection conn = DbFactory.CreateConnection(shardId);
var expression = Select.Name.Address.Orders();
var filtered = expression.Filter(Id.Equals(cid));
Customer result = conn.Execute(filtered);
// Save ke Memcached
memcache[cid] = result;
return result;
}
}
Siapa yg menggunakan Sharding?
Ebay menggunakan sharding, berikut kutipannya:
“…we split (or "shard") the data horizontally along its primary access path. User data, for example, is currently divided over 20 hosts, with each host containing 1/20 of the users. As our numbers of users grow, and as the data we store for each user grows, we add more hosts, and subdivide the users further. Again, we use the same approach for items, for purchases, for accounts, etc.”
Digg.com menggunakan sharding, berikut kutipannya:
“Digg only recently began sharding. While sharding is helping Digg.com achieve much faster performance overall, breaking a database into several smaller ones increases complexity, Ellis said. That can mean more work for developers and database administrators, because of the inability to use common SQL commands such as joining tables. "Developers don’t like this crazy stuff. That can create pushback," he said.”
Flickr, Youtube juga menggunakan sharding sebagai bagian dari architecture-nya.
Packaged Solution:
Sharding ini mau nggak mau harus manual di .NET dan SQL Server. Berikut beberapa produk yg akan sangat membantu proses sharding (yg bisa dicoba baru opensource, solusi Microsoft masih dalam tahap research):
- HBase: HBase's goal is the hosting of very large tables -- billions of rows X millions of columns -- atop clusters of commodity hardware. Try it if your plans for a data store run to big.
- Hypertable: Modeled after Google's well known Bigtable project, Hypertable is designed to manage the storage and processing of information on a large cluster of commodity servers, providing resilience to machine and component failures. Hypertable seeks to set the open source standard for highly available, petabyte scale, database systems.
- Microsoft Research Boxwood Project: The goal of this project is to explore the utility of data structures or abstractions as storage infrastructure. Storage infrastructure that allows incremental expansion has obvious economic benefits.
Pak, Infrastructure Layer-nya harus diubah
Tadi bapak bertanya apakah scalability dapat diubah tanpa mengubah aplikasi? Jawabannya bisa pak, selama kode Domain Layer (Application Core) tidak berisi kode-kode SQL seperti “SELECT * FROM Customers Where CustomerId = ‘1001’ “.
Kode-kode infrastructure seperti caching, database, file access, network calls, dsb. harus berada di Infrastructure Layer. Dari kode saya diatas, kode-kode Aplikasi Core menggunakan Repository untuk membuat seolah-olah semua objek diakses dari memory.
Lihat diagram berikut pak:
Dikarenakan kode database berada di Infrastructure Layer paling luar, maka layer dalam (Application Core) tidak perlu diubah. Tapi jika aplikasi bapak tidak mengenal Infrastructure Layer maka mungkin aplikasi bapak harus di-design ulang :)
--- end of conversation ---
Conclusion
Jadi gimana solusinya? Well, untuk sementara temporary solution masih dipakai (archive tiap 1 Terabytes) sambil saya membantu tim developers ISV ini me-refactor aplikasinya menjadi seperti Onion Architecture diatas. Saya juga akan meriset apakah kita akan menggunakan Memcached atau Microsoft Velocity untuk Distributed Hash Table.
Dan karena sang ISV tidak memiliki 1 Terabytes dummy data, sepertinya harus beberapa kali berkunjung ke customer site untuk mengetes improvement setelah menggunakan Distributed Hash Table, dan juga setelah Database Sharding.
Belum lagi masalah backup & restore jika Sharding terpilih menjadi opsi scalability.
Sepertinya kontrak consulting saya akan diperpanjang 2 tahun ke depan :)