Tes Performance Memcached

Di tulisan sebelumnya, saya sudah menyampaikan beberapa hal mengenai Memcached dan bagaimana implementasi Memcached dapat (berpotensi) mengurangi biaya server kamu. Di tulisan kali ini, saya akan coba membuat perbandingan performance antara aplikasi website yang tidak menggunakan Memcached dengan yang menggunakan Memcached.

Skenario Tes

Skenario tes yang akan saya lakukan sebetulnya cukup sederhana. Tes tersebut merupakan A/B test yang akan membuat simulasi load test sederhana menggunakan tool ab dan wrk. Endpoint yang akan menjadi parameter load test hanya melakukan query SQL SELECT sederhana, dimana query tersebut bertujuan untuk mengambil konfigurasi aplikasi yang disimpan di database. Skenario load test yang dilakukan adalah sebagai berikut:

  1. Mengirim 1000 sequential request: 1000 request akan diproses 1 per 1 secara bergantian oleh server

  2. Mengirim 1000 concurrent request: 1000 request akan diproses secara bersamaan oleh server

Skenario ini mungkin hanya simulasi yang sangat sederhana jika dibandingkan dengan skenario high traffic di dunia nyata. Namun setidaknya bisa memberi kita gambaran bagaimana dampak dari implementasi Memcached terhadap performance dari aplikasi kita.

Environment dan Alat Tes

Tes dilakukan dengan environment dan alat tes sebagai berikut:

  • Linux Mint 22, Intel Core i7-1255U 10 core/12 thread

  • nginx 1.24

  • PHP 8.3.9, Laravel 11.17.0

  • MS SQL Server 2022

  • memcached 1.6.24

  • ab 2.3

  • wrk 4.2.0

  • source code aplikasi yang digunakan untuk tes dapat diunduh disini

Berikut merupakan konfigurasi pool php-fpm yang digunakan:

pm = dynamic
pm.max_children = 50
pm.start_servers = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500

Hasil Tes

Tanpa Memcached: 1000 sequential request

Menggunakan ab

ab -n 1000 http://test-memcached.test/load-test/no-cache

This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking test-memcached.test (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.24.0
Server Hostname:        test-memcached.test
Server Port:            80

Document Path:          /load-test/no-cache
Document Length:        0 bytes

Concurrency Level:      1
Time taken for tests:   28.543 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      1140000 bytes
HTML transferred:       0 bytes
Requests per second:    35.03 [#/sec] (mean)
Time per request:       28.543 [ms] (mean)
Time per request:       28.543 [ms] (mean, across all concurrent requests)
Transfer rate:          39.00 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:    25   28   2.6     28      46
Waiting:       25   28   2.6     28      46
Total:         25   28   2.6     28      46

Percentage of the requests served within a certain time (ms)
  50%     28
  66%     29
  75%     29
  80%     29
  90%     31
  95%     33
  98%     37
  99%     41
 100%     46 (longest request)

Menggunakan wrk

wrk -t1 -c1 -d30s http://test-memcached.test/load-test/no-cache --latency

Running 30s test @ http://test-memcached.test/load-test/no-cache
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    28.82ms    2.78ms  50.81ms   82.13%
    Req/Sec    34.70      5.33    40.00     98.00%
  Latency Distribution
     50%   28.06ms
     75%   29.73ms
     90%   32.01ms
     99%   41.65ms
  1041 requests in 30.02s, 1.13MB read
Requests/sec:     34.67
Transfer/sec:     38.60KB
๐Ÿ’ก
Dari kedua tool tersebut, skenario 1000 sequential request tanpa memcached mendapatkan sekitar 30-35 requests per detik

Dengan Memcached: 1000 sequential request

Menggunakan ab

ab -n 1000 http://test-memcached.test/load-test/cache

This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking test-memcached.test (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.24.0
Server Hostname:        test-memcached.test
Server Port:            80

Document Path:          /load-test/cache
Document Length:        0 bytes

Concurrency Level:      1
Time taken for tests:   5.604 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      1140000 bytes
HTML transferred:       0 bytes
Requests per second:    178.44 [#/sec] (mean)
Time per request:       5.604 [ms] (mean)
Time per request:       5.604 [ms] (mean, across all concurrent requests)
Transfer rate:          198.66 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:     3    6   1.4      6      34
Waiting:        3    5   1.4      6      34
Total:          3    6   1.4      6      34

Percentage of the requests served within a certain time (ms)
  50%      6
  66%      6
  75%      6
  80%      6
  90%      7
  95%      8
  98%      8
  99%      9
 100%     34 (longest request)

Menggunakan wrk

wrk -t1 -c1 -d6s http://test-memcached.test/load-test/cache --latency

Running 6s test @ http://test-memcached.test/load-test/cache
  1 threads and 1 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.57ms    1.31ms  15.07ms   69.05%
    Req/Sec   180.05     24.36   240.00     68.33%
  Latency Distribution
     50%    5.57ms
     75%    6.07ms
     90%    7.14ms
     99%    9.56ms
  1078 requests in 6.01s, 1.17MB read
Requests/sec:    179.46
Transfer/sec:    199.79KB
๐Ÿ’ก
Dari kedua tool tersebut, skenario 1000 sequential request dengan memcached mendapatkan sekitar 178 requests per detik

Tanpa Memcached: 1000 concurrent request

Menggunakan ab

ab -n 1000 -c 1000 http://test-memcached.test/load-test/no-cache

This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking test-memcached.test (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.24.0
Server Hostname:        test-memcached.test
Server Port:            80

Document Path:          /load-test/no-cache
Document Length:        0 bytes

Concurrency Level:      1000
Time taken for tests:   28.368 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      1140000 bytes
HTML transferred:       0 bytes
Requests per second:    35.25 [#/sec] (mean)
Time per request:       28368.105 [ms] (mean)
Time per request:       28.368 [ms] (mean, across all concurrent requests)
Transfer rate:          39.24 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   13   0.5     13      14
Processing:    49 14715 8161.0  14715   28306
Waiting:       35 14715 8161.0  14715   28306
Total:         49 14728 8161.0  14728   28320

Percentage of the requests served within a certain time (ms)
  50%  14728
  66%  19243
  75%  21769
  80%  23213
  90%  26075
  95%  27521
  98%  28198
  99%  28274
 100%  28320 (longest request)

Menggunakan wrk

wrk -t12 -c1000 -d30s http://test-memcached.test/load-test/no-cache --latency

Running 30s test @ http://test-memcached.test/load-test/no-cache
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.29s   389.35ms   1.98s    58.82%
    Req/Sec    10.14      9.05    60.00     69.31%
  Latency Distribution
     50%    1.27s 
     75%    1.60s 
     90%    1.85s 
     99%    1.98s 
  1039 requests in 30.10s, 1.13MB read
  Socket errors: connect 0, read 0, write 0, timeout 988
Requests/sec:     34.52
Transfer/sec:     38.43KB
๐Ÿ’ก
Dari kedua tool tersebut, skenario 1000 concurrent request tanpa memcached mendapatkan sekitar 30-35 requests per detik

Dengan Memcached: 1000 concurrent request

Menggunakan ab

ab -n 1000 -c 1000 http://test-memcached.test/load-test/cache

This is ApacheBench, Version 2.3 <$Revision: 1903618 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking test-memcached.test (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:        nginx/1.24.0
Server Hostname:        test-memcached.test
Server Port:            80

Document Path:          /load-test/cache
Document Length:        0 bytes

Concurrency Level:      1000
Time taken for tests:   0.620 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      1140000 bytes
HTML transferred:       0 bytes
Requests per second:    1612.64 [#/sec] (mean)
Time per request:       620.100 [ms] (mean)
Time per request:       0.620 [ms] (mean, across all concurrent requests)
Transfer rate:          1795.33 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   12   0.7     12      13
Processing:    15  288 160.8    287     558
Waiting:       14  288 160.8    287     558
Total:         28  300 160.2    300     569

Percentage of the requests served within a certain time (ms)
  50%    300
  66%    390
  75%    440
  80%    468
  90%    523
  95%    553
  98%    566
  99%    568
 100%    569 (longest request)

Menggunakan wrk

wrk -t12 -c1000 -d1s http://test-memcached.test/load-test/cache --latency

Running 1s test @ http://test-memcached.test/load-test/cache
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   399.21ms  173.74ms 582.03ms   76.76%
    Req/Sec   225.81    238.78   808.00     81.25%
  Latency Distribution
     50%  483.46ms
     75%  549.28ms
     90%  557.41ms
     99%  569.83ms
  1790 requests in 1.06s, 1.95MB read
Requests/sec:   1684.16
Transfer/sec:      1.83MB
๐Ÿ’ก
Dari kedua tool tersebut, skenario 1000 concurrent request dengan memcached mendapatkan sekitar 1600-1700 requests per detik

Analisa Hasil

Saat menjalankan tes, selain terlihat perbedaan waktu respon dari aplikasi, saya juga melihat adanya perbedaan dalam penggunaan resource. Saat tidak menggunakan memcached, terlihat bahwa SQL Server paling banyak menggunakan CPU.

Penggunaan resource jika tidak menggunakan memcached

Ini menunjukkan bahwa setiap kali ada request, aplikasi langsung melakukan eksekusi query SQL pada engine SQL Server sehingga menyebabkan aplikasi lebih banyak menunggu respon dari SQL Server (bottleneck). Inilah yang menyebabkan mengapa pada skenario tanpa memcached, jumlah request/detik yang didapatkan relatif kecil.

Sedangkan pada saat menggunakan memcached, yang paling banyak menggunakan CPU adalah php-fpm.

Penggunaan resource saat menggunakan memcached

Pada saat menggunakan memcached, aplikasi tidak melakukan eksekusi query SQL dikarenakan hasil dari query tersebut masih disimpan dalam cache. Sehingga, utilisasi SQL Server tetap rendah dan resource CPU dapat digunakan sepenuhnya oleh php-fpm untuk merespon seluruh request yang datang.

Dari perbandingan di atas, terlihat bagaimana implementasi memcached dapat berpotensi mengurangi biaya server. Memcached dapat membantu mengurangi beban pada server database, sehingga resource yang dibutuhkan tidak setinggi jika dibandingkan tanpa menggunakan memcached.

Selain itu, karena aplikasi dapat menangani lebih banyak request, maka dapat meningkatkan user experience yang berpotensi dapat meningkatkan average revenue per user (ARPU). Soal ARPU ini mungkin kita bahas lebih detil di lain waktu ya :)

Tanpa MemcachedDengan Memcached
1000 sequential request30-35 request per detik178 request per detik
1000 concurrent request30-35 request per detik1600-1700 request per detik
Utilisasi CPU tertinggiSQL Serverphp-fpm
๐Ÿ’ก
DISCLAIMER: seperti yang sudah saya sebutkan sebelumnya, skenario tes ini mungkin tidak dapat 100% mencerminkan skenario high traffic di dunia nyata. Namun setidaknya dapat memberi gambaran bagaimana memcached dapat membantu mengurangi beban server tergantung bagaimana implementasinya. Mengenai tips dan strategi implementasi memcached, dapat ditemukan di bagian akhir tulisan ini.

Tips dan Strategi Implementasi

Untuk dapat memaksimalkan memcached dalam meningkatkan performance aplikasi, terdapat beberapa tips yang perlu diperhatikan:

  • Gunakan waktu expire yang dapat menyeimbangkan efisiensi cache dan relevansi data. Waktu expire yang terlalu panjang/lama dapat meningkatkan efisiensi cache, namun dapat berdampak pada data di cache yang sudah tidak relevan sehingga perlu di-refresh. Sedangkan waktu expire yang terlalu cepat dikhawatirkan tidak banyak membantu meningkatkan performance karena akan terlalu sering mengambil data baru.

  • Utamakan cache data yang read-intensive, bukan write-intensive. Jika kita cache data yang sering berubah nilainya, kita akan direpotkan karena harus sering melakukan refresh data. Utamakan cache data yang tidak terlalu sering berubah nilainya, contohnya seperti konfigurasi aplikasi.

  • Pantau penggunaan memcached. Konsisten memantau penggunaan memcached seperti berapa kapasitas memory yang sudah digunakan, tingkat hit/miss. Beberapa metrik tersebut dapat memberikan gambaran apakah strategi implementasi kita sudah tepat atau masih perlu perbaikan.

  • Gunakan memcached untuk menyimpan beberapa jenis objek. Selain dapat digunakan untuk menyimpan hasil query SQL seperti pada tes di atas, memcached juga dapat digunakan untuk menyimpan objek lain seperti halaman HTML dan sebagai media untuk menyimpan session.

Sebetulnya masih ada beberapa tips yang dapat dilakukan, namun hanya dengan mengikuti tips-tips dasar di atas sebetulnya sudah dapat memberikan dampak yang cukup signifikan. Selamat mencoba ya :)

0
Subscribe to my newsletter

Read articles from Rhezatama Dwi Rendragraha directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rhezatama Dwi Rendragraha
Rhezatama Dwi Rendragraha

A PHP wizard, SQL sorcerer, and Git ninja. Juggles nginx, PHP-FPM, and Linux bash like a circus act. 10+ years of turning coffee into code.