Bagaimana skala php5 + MySQL di atas 200 permintaan / detik?

16

Saya mengubah homepage saya untuk kinerja, saat ini menangani sekitar 200 permintaan / detik pada 3.14.by yang memakan 6 query SQL, dan 20 req / detik pada 3.14.by/forum yang merupakan forum phpBB.

Anehnya, jumlahnya hampir sama pada beberapa VPS dan dedicated server Atom 330.

Perangkat lunak server adalah sebagai berikut: Apache2 + mod_php prefork 4 childs (mencoba angka berbeda di sini), php5, APC, nginx, memcached untuk penyimpanan sesi PHP.

MySQL dikonfigurasikan untuk memakan sekitar 30% dari RAM yang tersedia (~ 150Mb pada VPS, 700Mb pada server khusus)

Sepertinya ada hambatan di tempat yang tidak memungkinkan saya untuk naik, saran? (Yaitu saya tahu bahwa melakukan kurang dari 6 SQL akan membuatnya lebih cepat, tetapi ini tidak terlihat seperti faktor pembatas, karena sqld makan tidak lebih dari beberapa% di atas karena permintaan cache)

Adakah yang menguji menendang apache2 yang sudah diprogram dan hanya menyisakan nginx + php yang lebih cepat?

Beberapa tolok ukur lagi

Small 40-byte static file: 1484 r/s via nginx+apache2, 2452 if we talk to apache2 directly. 
Small "Hello world" php script: 458 r/s via ngin+apache2.

Pembaruan: Tampaknya hambatan adalah kinerja MySQL pada data yang di-cache. Halaman dengan SQL tunggal menunjukkan 354req / detik, dengan 6 SQL - 180 req / detik. Menurut Anda, apa yang bisa saya ubah di sini? (Saya dapat membayar 100-200Mb untuk MySQL)

[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket      = /var/run/mysqld/mysqld.sock
nice        = 0

[mysqld]
default-character-set=cp1251
collation-server=cp1251_general_cs

skip-character-set-client-handshake

user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
skip-external-locking

bind-address        = 127.0.0.1

key_buffer      = 16M
max_allowed_packet  = 8M
thread_stack        = 64K
thread_cache_size   = 16
sort_buffer_size    = 8M
read_buffer_size    = 1M

myisam-recover      = BACKUP
max_connections        = 650
table_cache            = 256
thread_concurrency     = 10

query_cache_limit       = 1M
query_cache_size        = 16M

expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 8M

[mysql]
[isamchk]
key_buffer      = 8M

!includedir /etc/mysql/conf.d/
BarsMonster
sumber
Mengapa Anda menggunakan Apache dan nginx?
jamieb
Itu adalah konfigurasi umum, Apache2 ke PHP dan berbagai aplikasi yang membutuhkan infrastruktur apache, nginx untuk mengurangi jejak memori apache2 saat dimuat.
BarsMonster
Sebenarnya, saya tidak mengerti masalah Anda. Apakah situs Anda saat ini lambat? Jika demikian, seberapa lambat itu? Dan seberapa besar keinginan Anda untuk mempercepatnya? Sudahkah Anda mencoba membuat profil bagian mana pun dari situs Anda untuk menentukan di mana kemacetannya?
jamieb
Ada dalam deskripsi: Sekarang ada pada 180-200 permintaan / detik. Meskipun ini lebih dari cukup untuk beranda, saya ingin mengubah pengaturan ini agar situs lain yang dibangun di atas basis kode yang sama bekerja lebih cepat. Idealnya saya ingin menjenuhkan koneksi 100Mbit dengan halaman dinamis :-)
BarsMonster
2
"Permintaan per detik" sebenarnya bukan metrik yang bermakna dalam konteks ini. Netbook saya dapat menangani "200 permintaan per detik". Anda perlu memberi tahu kami berapa waktu respons yang ingin Anda capai di bawah tingkat koneksi seperti itu.
jamieb

Jawaban:

29

Jelas, ada banyak yang bisa Anda coba. Taruhan terbaik Anda adalah mengejar log Anda untuk kueri yang tidak menggunakan indeks (mengaktifkan log untuk itu) dan kueri yang tidak dioptimalkan lainnya. Saya telah menyusun daftar besar opsi terkait kinerja selama bertahun-tahun, jadi saya telah memasukkan sebagian kecil di sini untuk informasi Anda - semoga membantu. Berikut adalah beberapa catatan umum untuk hal-hal yang dapat Anda coba (jika belum):

MySQL

  • query_cache_type = 1 - cache SQL queries aktif. Jika diatur ke 2, kueri hanya di-cache jika petunjuk SQL_CACHE diteruskan kepada mereka. Demikian pula dengan tipe 1, Anda dapat menonaktifkan cache untuk permintaan tertentu dengan petunjuk SQL_NO_CACHE
  • key_buffer_size = 128M (default: 8M) - buffer memori untuk indeks tabel MyISAM. Pada dedicated server, bertujuan untuk mengatur key_buffer_size ke setidaknya seperempat, tetapi tidak lebih dari setengah, dari jumlah total memori di server
  • query_cache_size = 64M (default: 0) - ukuran cache kueri
  • back_log = 100 (default: 50, maks: 65535) - Antrian permintaan koneksi luar biasa. Hanya masalah ketika ada banyak koneksi dalam waktu singkat
  • join_buffer_size = 1M (default: 131072) - buffer yang digunakan saat melakukan pemindaian tabel penuh (tidak ada indeks)
  • table_cache = 2048 (default: 256) - seharusnya max_user_connections dikalikan dengan jumlah maksimum BERGABUNG dengan kueri SQL terberat Anda. Gunakan variabel "open_tables" pada waktu puncak sebagai panduan. Lihat juga variabel "opens_tables" - harus dekat dengan "open_tables"
  • query_prealloc_size = 32K (default: 8K) - memori persisten untuk parsing dan eksekusi pernyataan. Tingkatkan jika memiliki kueri yang kompleks
  • sort_buffer_size = 16M (default: 2M) - membantu menyortir (ORDER BY dan GROUP BY operasi)
  • read_buffer_size = 2M (default: 128K) - Membantu dengan pemindaian berurutan. Tambah jika ada banyak pemindaian berurutan.
  • read_rnd_buffer_size = 4M - membantu tabel MyISAM mempercepat membaca setelah sortir
  • max_length_for_sort_data - ukuran baris untuk menyimpan alih-alih penunjuk baris dalam file sortir. Dapat menghindari pembacaan tabel acak
  • key_cache_age_threshold = 3000 (default: 300) - waktu untuk menyimpan cache kunci di zona panas (sebelum diturunkan ke pemanasan)
  • key_cache_division_limit = 50 (default: 100) - memungkinkan mekanisme penggusiran cache yang lebih canggih (dua level). Menunjukkan persentase yang harus disimpan untuk level bawah. delay_key_write = ALL - buffer kunci tidak dibilas untuk tabel pada setiap pembaruan indeks, tetapi hanya ketika tabel ditutup. Ini mempercepat menulis pada tombol, tetapi jika Anda menggunakan fitur ini, Anda harus menambahkan pemeriksaan otomatis dari semua tabel MyISAM dengan memulai server dengan opsi --myisam-recover = BACKUP, FORCE
  • memlock = 1 - proses kunci dalam memori (untuk mengurangi swapping in / out)

Apache

  • ubah metode pemijahan (ke mpm misalnya)
  • nonaktifkan log jika memungkinkan
  • AllowOverride None - bila memungkinkan, nonaktifkan .htaccess. Itu menghentikan apache untuk mencari file .htaccess jika mereka tidak digunakan sehingga menyimpan permintaan pencarian file
  • SendBufferSize - Diatur ke OS default. Pada jaringan yang padat, Anda harus mengatur parameter ini mendekati ukuran file terbesar yang biasanya diunduh
  • KeepAlive Off (Aktif Aktif) - dan instal lingerd untuk menutup koneksi jaringan dengan benar dan lebih cepat
  • DirectoryIndex index.php - Menyimpan daftar file sesingkat dan absolut mungkin.
  • Opsi FollowSymLinks - untuk menyederhanakan proses akses file di Apache
  • Hindari menggunakan mod_rewrite atau setidaknya regex kompleks
  • ServerToken = prod

PHP

  • variable_order = "GPCS" (Jika Anda tidak memerlukan variabel lingkungan)
  • register_globals = Tidak aktif - selain dari risiko keamanan, itu juga memiliki dampak kinerja
  • Usahakan include_path seminimal mungkin (menghindari pencarian sistem file tambahan)
  • display_errors = Off - Nonaktifkan menampilkan kesalahan. Sangat disarankan untuk semua server produksi (tidak menampilkan pesan kesalahan jika terjadi masalah).
  • magic_quotes_gpc = Mati
  • magic_quotes _ * = Mati
  • output_buffering = Hidup
  • Nonaktifkan logging jika memungkinkan
  • expose_php = Mati
  • register_argc_argv = Mati
  • always_populate_raw_post_data = Mati
  • letakkan file php.ini di mana php akan mencarinya terlebih dahulu.
  • session.gc_divisor = 1000 atau 10000
  • session.save_path = "N; / path" - Untuk situs besar, pertimbangkan untuk menggunakannya. Pisahkan file sesi menjadi subdirektori

OS Tweaks

  • Pasang hard disk bekas dengan opsi -o noatime (tanpa waktu akses). Juga tambahkan opsi ini ke file / etc / fstab.
  • Tweak / proc / sys / vm / swappiness (dari 0 hingga 100) untuk melihat apa yang memiliki hasil terbaik
  • Gunakan RAM Disks - mount --bind -ttmpfs / tmp / tmp
Ivan Peevski
sumber
Itu daftar yang bagus, saya sudah memiliki sebagian besar dari ini, dan ketika saya menambahkan hal-hal yang tersisa, kinerja belum meningkat. Sepertinya hambatan di suatu tempat antara PHP dan MySQL tidak dapat menangani lebih dari 800 permintaan per detik dari cache kueri ...
BarsMonster
Oke, bagaimana Anda terhubung ke database (mysql_pconnect () alih-alih mysql_connect ())? Apakah Anda menggunakan koneksi persisten? coba keduanya ...
Ivan Peevski
Saya sudah menggunakan pconnect dan koneksi
pool
Hanya untuk tingkat kelengkapan, saya akan coba sambungkan saja. Saya telah melihat kasus (terutama dalam pengujian beban) di mana kinerjanya lebih baik.
Ivan Peevski
1

Jika hambatannya bukan CPU, maka IO - baik jaringan atau disk. Jadi .. Anda perlu melihat berapa banyak IO yang terjadi. Saya tidak akan mengira itu adalah jaringannya (kecuali Anda berada pada tautan setengah dupleks 10mbps, tetapi ada baiknya memeriksa sakelar kalau-kalau deteksi otomatis tidak melakukan tugasnya dengan benar).

Itu meninggalkan disk IO, yang bisa menjadi faktor besar terutama pada VPS. Gunakan sar atau iostat untuk melihat cakram, kemudian google cara menemukan detail lebih lanjut jika cakram Anda banyak digunakan.

gbjbaanb
sumber
Ya, Jaringan bukan masalah - ketika menjalankan ab dari server lokal, kinerjanya sama saja. Saya telah memeriksa waktu iowait - di bawah 0,01% - pada dasarnya semuanya ada di cache disk, dan tidak ada disk yang terlibat dalam pemrosesan permintaan (semua log dinonaktifkan).
BarsMonster
1

Saya akan mencari caching dengan Nginx ( memcached ) atau Varnish .

Paling tidak Anda harus server file statis dengan Nginx seperti kata SaveTheRbtz.

Espennilsen
sumber
Ini adalah halaman yang dinamis, jadi saya lebih suka untuk tidak menyimpannya.
BarsMonster
1
memcached bukan aplikasi caching tradisional dan dapat bekerja luar biasa untuk halaman dinamis. Itu terletak di antara DB dan aplikasi Anda. Anda aplikasi pertama memcached query untuk suatu objek, jika tidak ada di sana, maka itu diambil dari DB. Efek bersihnya adalah Anda menggunakan RAM untuk melayani permintaan DB Anda dan bukannya penyimpanan persisten yang jauh lebih lambat pada DB.
jamieb
Memcache dapat digunakan dengan nginx, fitur yang dikenal. Penyimpanan persisten yang lebih lambat tidak digunakan, semuanya ada dalam cache kueri di MySQL.
BarsMonster
Memcached dan cache query MySQL tidak bisa dibandingkan; mereka bahkan tidak melakukan hal yang sama. Anda cukup cepat untuk menembak hampir semua saran yang diposting di sini tanpa repot-repot memahaminya. Saya sarankan Anda menjadi sedikit lebih berpikiran terbuka.
jamieb
Saya jelas memahami perbedaan antara cache kueri memcached dan MySQL. Tetapi karena fakta bahwa semuanya ada dalam cache Query dengan rasio hit 100%, saya tidak akan menyebutnya "penyimpanan persisten lambat". Jawaban asli kemarin adalah tentang menggunakan NginX + Memcached yang merupakan skenario yang cukup umum untuk meng-cache seluruh halaman. Caching objek individu adalah skenario lain yang sama sekali berbeda. Saat menggunakan memcached di depan MySQL ada di atas meja, saya berpikir untuk mendapatkan lebih banyak jus tanpa itu untuk saat ini (karena akan membutuhkan sedikit perubahan kode).
BarsMonster
1

Karena server sepertinya tidak menjadi masalah, mungkin generator bebannya. Cobalah untuk menjalankannya di beberapa mesin.

Oliver
sumber
Performanya sama bahkan jika saya menjalankannya dari server itu sendiri. Tidak peduli seberapa manu koneksi konkuren - 10 atau 50. Pengujian beban dilakukan melalui ab -c 10 -t 10
BarsMonster
1

Kedengarannya bagi saya seperti Anda mungkin memukul jumlah maksimum koneksi yang diizinkan oleh Apache. Lihatlah konfigurasi Apache Anda. Meningkatkan batas server dan klien maks akan membantu jika Anda belum terikat oleh batas lain seperti I / O atau memori. Lihatlah nilai yang ada untuk mpm_prefork_module atau mpm_worker_module dan sesuaikan sesuai dengan kebutuhan Anda.

ServerLimit 512
MaxClients 512
Erik Giberti
sumber
Nah, apakah saya benar-benar memerlukan ini asalkan saya memiliki nginx di depan apache2, jadi saya percaya tidak ada banyak perasaan memiliki lebih dari inti fisik * 2 proses Apache2 ....
BarsMonster
Baru saja memverifikasi ini. Peningkatan jumlah proses Apache2 dari 4 menjadi 16 tidak meningkatkan kinerja sama sekali (bahkan turun 0,5%). Peningkatan jumlah pekerja nginx menjadi 2 atau 4 tidak meningkatkan apa pun.
BarsMonster
1
Jika data Anda cukup statis, artinya tidak memperbarui setiap pemuatan laman lainnya, Anda dapat meningkatkan kueri_cache Anda. MySQL akan berpegang pada hasil yang disetel seperti itu dan menarik dari memori. Namun, jika tabel yang sedang di-cache menerima tulisan apa pun selama waktu itu, itu membuat cache tidak valid (bahkan jika data tidak terpengaruh), menjadikannya membuang-buang memori.
Erik Giberti
Saat ini saya melihat rasio hit cache cache 100%, dan MySQL masih terasa lambat ...
BarsMonster
1
Tambahkan nama-lompati ke file konfigurasi MySQL Anda. Itu akan menghemat pencarian DNS pada setiap koneksi ke server. Kekurangannya di sini adalah bahwa semua koneksi harus dikunci oleh IP (dengan asumsi Anda tidak menggunakan '%'). Jika SQL ada di server yang sama dan tidak perlu diakses di mana pun selain localhost, Anda juga bisa menambahkan skip-networking untuk mematikan seluruh tumpukan TCP / IP. Namun, saya pikir hambatannya adalah Apache.
Erik Giberti
0

Apakah beban ini dihasilkan oleh alat atau beban dunia nyata?

Anda mungkin ingin memeriksa memcached. Saya telah melihat masalah pada tingkat koneksi yang tinggi yang menyebabkan latensi dalam aplikasi.

Jika menggunakan generator beban, apa yang Anda dapatkan ketika menekan halaman statis kecil?

Selama pemuatan, Anda mungkin ingin memeriksa tumpukan jaringan untuk kondisi TIME_WAIT. Mungkin Anda sedang mengisi antrian koneksi Anda.

Ada sekitar 100 alasan dan item yang dapat Anda lihat tetapi tanpa informasi lebih lanjut, saya hanya membuang tebakan pada saat ini.

jeffatrackaid
sumber
Ini diuji melalui ab-c 10 -t 10 URL Saya membuat benchmark dari server itu sendiri, jadi jaringan seharusnya tidak menjadi masalah. Saya telah memposting lebih banyak tolok ukur sesuai permintaan Anda.
BarsMonster
Saya tidak akan menghabiskan terlalu banyak upaya menyetel dengan ab. Anda mungkin menemukan itu tidak diterjemahkan dengan baik ke dalam kinerja dunia nyata. Yang mungkin ingin Anda lakukan adalah membedah aplikasi Anda dan menguji setiap komponen. Misalnya, tekan server apache secara langsung hanya dengan halaman statis yang sangat kecil. Ini akan memberi Anda dan gagasan max req / detik Anda di backend. Letakkan nginx di depan, uji ulang memanggil file backend yang sama. Kemudian uji dengan halaman php sederhana "hello world". Terkadang semua layer bisa menutupi sesuatu yang sederhana. Juga, perhatikan koneksi selama tes. Pastikan tumpukan jaringan Anda tidak terisi.
jeffatrackaid
Saya melakukan tolok ukur ini kemarin, dan semuanya ada dalam uraian pertanyaan awal yang diperbarui. Juga, tes dilakukan di localhost, jadi jaringan tidak menjadi masalah.
BarsMonster
Jaringan dapat menjadi masalah bahkan ketika dilakukan pada host lokal. Tidak mungkin dalam kasus Anda tetapi dapat menyebabkan masalah. Paling tidak sekarang Anda memiliki batas atas ~ 450 req / detik dengan pengaturan PHP Anda saat ini. Langkah selanjutnya adalah menjatuhkan panggilan basis data dan melihat bagaimana itu berubah. Saya suka memecah ini ketika melakukan penyetelan tingkat tinggi karena ini benar-benar dapat membantu Anda menentukan lapisan yang menyebabkan sebagian besar masalah.
jeffatrackaid
-1

99% persen dari masalah waktu seperti ini akan ditelusuri kembali ke database. Pastikan indeks memukul Anda terlebih dahulu. Jika itu tidak berhasil, mulai caching semua yang Anda bisa.


sumber
Itu semua indeks dan seperti yang saya katakan, itu bahkan memukul cache kueri MySQL dalam 100% kasus
BarsMonster
-1

Saya sarankan Anda untuk menggunakan (jika mungkin) pooler koneksi untuk menjaga database terhubung ke aplikasi web Anda (tidak perlu menyambung kembali pada setiap permintaan). Itu bisa membuat perbedaan kecepatan yang sangat besar.

Juga, cobalah menganalisis semua pertanyaan Anda dengan EXPLAIN (dan mengapa tidak membuat profil pertanyaan Anda dengan SHOW PROFILE?).

Kedare
sumber
Semua kueri menggunakan indeks. Kumpulan koneksi MySQL digunakan.
BarsMonster