Saya ragu memaksimalkan penggunaan CPU Redis akan menguntungkan desain backend Anda. Pertanyaan yang tepat adalah apakah Redis cukup efisien untuk mempertahankan throughput Anda pada latensi tertentu. Redis adalah server single-threaded:pada konsumsi CPU 80%, latensi kemungkinan akan sangat buruk.
Saya sarankan Anda mengukur latensi saat redis-benchmark berfungsi untuk melihat apakah itu dapat diterima untuk kebutuhan Anda sebelum mencoba meningkatkan konsumsi CPU Redis. Opsi --latency dari redis-cli dapat digunakan untuk ini:
- meluncurkan redis-server
- coba redis-cli --latency, catat nilai rata-rata, hentikan
- di jendela lain, mulai tolok ukur, dan pastikan berjalan sebentar
- coba redis-cli --latency, catat nilai rata-rata, hentikan
- hentikan tolok ukur
- bandingkan dua nilai rata-rata
Sekarang, jika Anda benar-benar ingin meningkatkan konsumsi CPU Redis, Anda memerlukan program klien yang efisien (seperti redis-benchmark), yang mampu menangani beberapa koneksi secara bersamaan, baik beberapa contoh program klien Anda.
Lua adalah bahasa yang ditafsirkan dengan cepat, tetapi masih merupakan bahasa yang ditafsirkan. Ini akan menjadi satu atau dua kali lipat lebih lambat dari kode C. Redis jauh lebih cepat dalam mem-parsing/menghasilkan protokolnya daripada lua-redis, jadi Anda tidak akan dapat memenuhi Redis dengan klien Lua yang unik (kecuali jika Anda menggunakan perintah O(n) Redis - lihat nanti).
webdis diimplementasikan dalam C, dengan pustaka klien yang efisien, tetapi harus menguraikan protokol http/json yang kebetulan lebih bertele-tele dan kompleks daripada protokol Redis. Kemungkinan mengkonsumsi lebih banyak CPU daripada Redis itu sendiri untuk sebagian besar operasi. Jadi sekali lagi, Anda tidak akan memenuhi Redis dengan satu instance webdis.
Berikut adalah beberapa contoh untuk menjenuhkan Redis dengan beberapa klien Lua.
Jika belum, saya sarankan Anda melihat halaman benchmark Redis terlebih dahulu.
Jika Anda menjalankan tolok ukur pada kotak yang sama dengan Redis:
Poin kuncinya adalah mendedikasikan inti untuk Redis, dan menjalankan program klien pada inti lainnya. Di Linux, Anda dapat menggunakan perintah taskset untuk ini.
# Start Redis on core 0
taskset -c 0 redis-server redis.conf
# Start Lua programs on the other cores
for x in `seq 1 10` ; do taskset -c 1,2,3 luajit example.lua & done
Program Lua harus menggunakan pipelining untuk memaksimalkan throughput dan mengurangi aktivitas sistem.
local redis = require 'redis'
local client = redis.connect('127.0.0.1', 6379)
for i=1,1000000 do
local replies = client:pipeline(function(p)
for j=1,1000 do
local key = 'counter:'..tostring(j)
p:incrby(key,1)
end
end)
end
Di sistem saya, program Lua membutuhkan lebih dari 4 kali CPU Redis, jadi Anda memerlukan lebih dari 4 core untuk menjenuhkan Redis dengan metode ini (kotak 6 core seharusnya baik-baik saja).
Jika Anda menjalankan tolok ukur pada kotak yang berbeda dari Redis:
Kecuali jika Anda menjalankan mesin virtual yang kekurangan CPU, hambatan kemungkinan adalah jaringan dalam kasus itu. Saya rasa Anda tidak dapat memenuhi Redis dengan tautan kurang dari 1 GbE.
Pastikan untuk menyalurkan kueri Anda sejauh mungkin (lihat program Lua sebelumnya) untuk menghindari kemacetan latensi jaringan, dan mengurangi biaya interupsi jaringan pada CPU (mengisi paket ethernet). Coba jalankan Redis pada inti yang tidak terikat ke kartu jaringan (dan proses interupsi jaringan). Anda dapat menggunakan alat seperti htop untuk memeriksa poin terakhir ini.
Cobalah untuk menjalankan klien Lua Anda di berbagai mesin lain dari jaringan jika Anda bisa. Sekali lagi Anda akan membutuhkan banyak klien Lua untuk memenuhi Redis (6-10 seharusnya baik-baik saja).
Dalam beberapa kasus, proses Lua yang unik sudah cukup:
Sekarang, dimungkinkan untuk menjenuhkan Redis dengan satu klien Lua jika setiap kueri cukup mahal. Ini contohnya:
local redis = require 'redis'
local client = redis.connect('127.0.0.1', 6379)
for i=1,1000 do
local replies = client:pipeline(function(p)
for j=1,1000 do
p:rpush("toto",i*1000+j)
end
end)
end
N = 500000
for i=1,100000 do
local replies = client:pipeline(function(p)
for j=1,10 do
p:lrange("toto",N, N+10)
end
end)
end
Program ini mengisi daftar dengan 1 juta item, dan kemudian menggunakan perintah lrange untuk mengambil 10 item dari tengah daftar (kasus terburuk untuk Redis). Jadi setiap kali kueri dijalankan, 500 ribu item dipindai oleh server. Karena hanya 10 item yang dikembalikan, mereka cepat diurai oleh lua-redis yang tidak akan menggunakan CPU. Dalam situasi ini, semua konsumsi CPU akan berada di sisi server.
Kata-kata terakhir
Mungkin ada klien Redis yang lebih cepat daripada redis-lua:
- https://github.com/agladysh/lua-hiredis (berdasarkan yang disewa)
- https://github.com/agladysh/ljffi-hiredis (berdasarkan yang disewa, menggunakan luajit FFI)
Anda mungkin ingin mencobanya.