php5 + MySQL을 초당 200 회 이상 요청하는 방법은 무엇입니까?


16

나는 홈페이지를 성능 조정을 위해 조정하고 있으며, 현재 3.14.by에서 약 200 개의 요청을 처리하여 6 개의 SQL 쿼리를 사용하고 3.14.by/forum에서 20 req / second를 phpBB 포럼입니다.

이상하게도 일부 VPS 및 전용 Atom 330 서버에서 숫자는 거의 같습니다.

서버 소프트웨어는 다음과 같습니다 : Apache2 + mod_php prefork 4 개의 자식 (여기서 다른 숫자를 시도), php5, APC, nginx, PHP 세션 저장을위한 memcached.

MySQL은 사용 가능한 RAM의 약 30 %를 소비하도록 구성됩니다 (VPS에서 ~ 150Mb, 전용 서버에서 700Mb)

이것은 어딘가에 병목 현상이있는 것 같습니다. 어떤 제안도 있습니까? (즉, 6 개 미만의 SQL을 수행하면 더 빠를 수 있지만 sqld는 캐시 된 쿼리로 인해 최상위 %를 몇 % 이상 먹지 않으므로 제한 요소처럼 보이지 않습니다)

누구든지 preforked apache2를 차고 nginx + php를 떠나는 것이 훨씬 빠르다는 것을 테스트 했습니까?

더 많은 벤치 마크

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.

업데이트 : 병목 현상은 캐시 된 데이터의 MySQL 성능으로 나타납니다. 단일 SQL이 포함 된 페이지는 354req / sec를 표시하고 6 개의 SQL은-180 req / sec입니다. 내가 여기서 조정할 수 있다고 생각하십니까? (MySQL의 경우 100-200Mb를 포크 아웃 할 수 있음)

[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/

왜 Apache와 nginx를 모두 사용하고 있습니까?
— jamieb

이는 일반적인 구성, Apache2에서 PHP 및 아파치 인프라를 필요로하는 다양한 앱,로드시 아파치 2 메모리 풋 프린트를 줄이기위한 nginx입니다.
— BarsMonster

사실, 나는 당신의 문제를 이해하지 못합니다. 귀하의 사이트가 현재 느린가요? 그렇다면 얼마나 느립니까? 그리고 속도를 얼마나 높이고 싶습니까? 병목 현상의 위치를 ​​파악하기 위해 사이트의 일부를 프로파일 링하려고 했습니까?
— jamieb

설명에 있습니다 : 이제 180-200 요청 / 초입니다. 이것이 홈페이지에는 충분하지만 동일한 코드베이스로 작성된 다른 사이트가 더 빨리 작동하도록이 설정을 조정하고 싶습니다. 이상적으로 동적 페이지로 100Mbit 연결을 포화시키고 싶습니다 :-)
— BarsMonster

2
"초당 요청 수"는이 맥락에서 실제로 의미있는 지표가 아닙니다. 내 넷북은 "초당 200 개의 요청"을 처리 할 수 ​​있습니다. 이러한 연결 속도에서 원하는 응답 시간을 알려주십시오.
— jamieb

답변:


29

분명히, 당신이 시도 할 수있는 많은 것이 있습니다. 최선의 방법은 인덱스를 사용하지 않는 쿼리 (그에 대한 로그 사용) 및 최적화되지 않은 다른 쿼리에 대한 로그를 추적하는 것입니다. 나는 수년간 성능 관련 옵션의 거대한 목록을 작성 했으므로 귀하의 정보에 대한 작은 하위 집합을 여기에 포함 시켰습니다. 시도해 볼 수있는 일반적인 참고 사항은 다음과 같습니다 (아직하지 않은 경우).

MySQL

  • query_cache_type = 1-캐시 SQL 쿼리가 켜져 있습니다. 2로 설정하면 SQL_CACHE 힌트가 전달 된 경우에만 쿼리가 캐시됩니다. 유형 1과 마찬가지로 SQL_NO_CACHE 힌트를 사용하여 특정 쿼리에 대해 캐시를 비활성화 할 수 있습니다
  • key_buffer_size = 128M (기본값 : 8M)-MyISAM 테이블 인덱스를위한 메모리 버퍼. 전용 서버에서 key_buffer_size를 서버의 총 메모리 양의 1/4 이상으로 절반 이하로 설정하십시오.
  • query_cache_size = 64M (기본값 : 0)-쿼리 캐시 크기
  • back_log = 100 (기본값 : 50, 최대 : 65535)-미해결 연결 요청 대기열. 단시간에 많은 연결이있을 때만 중요
  • join_buffer_size = 1M (기본값 : 131072)-전체 테이블 스캔 (인덱스 없음)이있을 때 사용되는 버퍼
  • table_cache = 2048 (기본값 : 256)-max_user_connections에 가장 큰 SQL 쿼리에 포함 된 최대 JOIN 수를 곱해야합니다. 피크 시간에는 "open_tables"변수를 지침으로 사용하십시오. 또한 "opened_tables"변수를보십시오- "open_tables"에 가까워 야합니다
  • query_prealloc_size = 32K (기본값 : 8K)-구문 분석 및 실행을위한 영구 메모리. 복잡한 쿼리가있는 경우 증가
  • sort_buffer_size = 16M (기본값 : 2M)-정렬을 도와줍니다 (ORDER BY 및 GROUP BY 작업)
  • read_buffer_size = 2M (기본값 : 128K)-순차적 스캔에 도움이됩니다. 순차적 스캔이 많은 경우 늘리십시오.
  • read_rnd_buffer_size = 4M-정렬 후 MyISAM 테이블의 읽기 속도 향상
  • max_length_for_sort_data-정렬 파일에서 행 포인터 대신 저장할 행 크기 임의의 테이블 읽기를 피할 수 있습니다
  • key_cache_age_threshold = 3000 (기본값 : 300)-키 캐시를 핫존에 보관하는 시간 (가열하기 전에)
  • key_cache_division_limit = 50 (기본값 : 100)-보다 정교한 캐시 제거 메커니즘을 활성화합니다 (2 단계). 최저 수준으로 유지할 비율을 나타냅니다. delay_key_write = ALL-모든 인덱스 업데이트에서 테이블에 대해 키 버퍼가 플러시되지 않고 테이블이 닫힐 때만 해당됩니다. 이렇게하면 키 쓰기 속도가 크게 향상되지만이 기능을 사용하는 경우 --myisam-recover = BACKUP, FORCE 옵션으로 서버를 시작하여 모든 MyISAM 테이블의 자동 검사를 추가해야합니다.
  • memlock = 1-메모리에서 프로세스 잠금 (스왑 인 / 아웃 감소)

아파치

  • 스폰 방법 변경 (예 : mpm)
  • 가능하면 로그 비활성화
  • AllowOverride 없음-가능할 때마다 .htaccess를 비활성화합니다. 사용하지 않으면 .htaccess 파일을 찾기 위해 아파치를 중지하므로 파일 조회 요청을 저장합니다.
  • SendBufferSize-OS 기본값으로 설정합니다. 혼잡 한 네트워크에서는이 매개 변수를 일반적으로 다운로드 한 가장 큰 파일의 크기에 가깝게 설정해야합니다
  • KeepAlive Off (기본값 On)-네트워크 연결을 올바르게 닫고 더 빠른 속도로 lingerd를 설치하십시오.
  • DirectoryIndex index.php-파일 목록을 가능한 짧고 절대적으로 유지하십시오.
  • 옵션 FollowSymLinks-Apache에서 파일 액세스 프로세스를 단순화
  • mod_rewrite 또는 최소한 복잡한 정규식 사용을 피하십시오
  • ServerToken = 제품

PHP

  • variables_order = "GPCS"(환경 변수가 필요하지 않은 경우)
  • register_globals = 끄기-보안 위험이 아니라 성능에 영향을 미칩니다
  • include_path를 가능한 최소한으로 유지하십시오 (추가 파일 시스템 조회는 피하십시오)
  • display_errors = 끄기-오류 표시를 비활성화합니다. 모든 프로덕션 서버에 강력히 권장됩니다 (문제 발생시 추악한 오류 메시지가 표시되지 않음).
  • magic_quotes_gpc = 해제
  • magic_quotes _ * = 끄기
  • output_buffering = 켜기
  • 가능하면 로깅 비활성화
  • expose_php = 끄기
  • register_argc_argv = 해제
  • always_populate_raw_post_data = 끄기
  • php.ini 파일을 php가 먼저 찾을 위치에 배치하십시오.
  • session.gc_divisor = 1000 또는 10000
  • session.save_path = "N; / path"-대규모 사이트의 경우 사용을 고려하십시오. 세션 파일을 하위 디렉토리로 분할

OS 비틀기

  • 사용 된 하드 디스크를 -o noatime 옵션 (액세스 시간 없음)으로 마운트하십시오. 또한이 옵션을 / etc / fstab 파일에 추가하십시오.
  • / proc / sys / vm / swappiness (0에서 100까지)를 조정하여 최상의 결과가 무엇인지 확인하십시오.
  • RAM 디스크 사용-mount --bind -ttmpfs / tmp / tmp

그것은 좋은 목록입니다, 나는 이미 이것들의 대다수를 가졌으며, 나머지 것들을 추가했을 때 성능은 향상되지 않았습니다. 병목 현상은 PHP와 MySQL 사이에서 쿼리 캐시에서 초당 800 개 이상의 요청을 처리 할 수없는 것처럼 보입니다.
— BarsMonster

좋아, 어떻게 데이터베이스에 연결합니까 (mysql_connect () 대신 mysql_pconnect ())? 영구 연결을 사용합니까? 두 가지 방법 모두 시도 ...
— Ivan Peevski

나는 이미 pconnect에 있고 연결 풀링은 php.ini에서 활성화되어 있습니다 ... : -S
— BarsMonster

완전 함을 위해 연결을 시도합니다. 나는 그것이 더 잘 수행되는 경우 (특히로드 테스트에서)를 보았습니다.
— Ivan Peevski

1

병목 현상이 CPU가 아닌 경우 IO는 네트워크 또는 디스크입니다. 그래서 .. 당신은 얼마나 많은 IO가 진행되고 있는지 볼 필요가 있습니다. 나는 네트워크를 생각하지 않았을 것입니다 (10mbps 반이중 링크를 사용하지 않는 한 자동 감지가 제대로 작동하지 않는 경우 스위치를 확인하는 것이 좋습니다).

디스크 IO가 남게되는데 이는 특히 VPS에서 큰 요소가 될 수 있습니다. sar 또는 iostat를 사용하여 디스크를 살펴본 다음 디스크를 많이 사용하는 경우 자세한 정보를 찾는 방법을 Google에 찾으십시오.


예, 네트워크에는 문제가 없습니다. 로컬 서버에서 ab를 실행할 때 성능은 동일합니다. iowait 시간을 확인했습니다-0.01 % 미만입니다. 기본적으로 모든 것이 디스크 캐시에 있으며 요청 처리와 관련된 디스크 쓰기가 없습니다 (모든 로그가 비활성화되어 있음).
— BarsMonster

1

Nginx ( memcached ) 또는 Varnish 를 사용하여 캐싱을 조사합니다 .

최소한 SaveTheRbtz와 같이 Nginx를 사용하여 정적 파일을 서버에 저장해야합니다.


이들은 동적 페이지이므로 캐시하지 않는 것이 좋습니다.
— BarsMonster

1
memcached는 전통적인 캐싱 앱이 아니며 동적 페이지에서 놀라운 일을 할 수 있습니다. DB와 앱 사이에 있습니다. 앱은 먼저 객체에 대해 memcached를 쿼리합니다. 객체가 없으면 DB에서로드됩니다. 결과적으로 DB에서 훨씬 느린 영구 스토리지가 아닌 RAM을 사용하여 DB 요청을 처리하는 것입니다.
— jamieb

Memcache는 알려진 기능인 nginx와 함께 사용할 수 있습니다. 느린 영구 저장소는 사용되지 않으며 MySQL의 쿼리 캐시에 모두 있습니다.
— BarsMonster

Memcached와 MySQL의 쿼리 캐시는 실제로 비교할 수 없습니다. 그들은 똑같은 일조차하지 않습니다. 여기에 게시 된 모든 제안을 이해하기 위해 귀찮게하지 않고 꽤 빨리 처리 할 수 ​​있습니다. 좀 더 열린 마음을 갖는 것이 좋습니다.
— jamieb

memcached와 MySQL 쿼리 캐시의 차이점을 분명히 이해합니다. 그러나 모든 것이 100 % 적중률로 쿼리 캐시에 있다는 사실 때문에 "느린 영구 저장 장치"라고 부르지 않습니다. 어제의 원래 대답은 전체 페이지를 캐시하는 일반적인 시나리오 인 NginX + Memcached를 사용하는 것에 관한 것입니다. 개별 객체 캐싱은 완전히 다른 시나리오입니다. MySQL 앞에서 memcached를 사용하는 것이 표에 있지만 지금은 많은 코드 변경이 필요하기 때문에 더 많은 주스를 얻는 것에 대해 생각하고 있습니다.
— BarsMonster 5

1

서버에 문제가없는 것 같으므로로드 생성기가 문제 일 수 있습니다. 여러 컴퓨터에서 실행하십시오.


서버 자체에서 실행해도 성능은 같습니다. manu 동시 연결 방식 (10 또는 50)에 관계없이로드 테스트는 ab -c 10 -t 10을 통해 수행됩니다.
— BarsMonster

1

Apache가 허용하는 최대 연결 수에 도달 한 것 같습니다. Apache 구성을 살펴보십시오. 서버 제한 및 최대 클라이언트를 늘리면 I / O 또는 메모리와 같은 다른 제한에 아직 구속되지 않은 경우 도움이됩니다. mpm_prefork_module 또는 mpm_worker_module의 값을보고 필요에 맞게 적절히 조정하십시오.

서버 제한 512
MaxClients 512

아파치 2 앞에 nginx가 있다면 이것을 정말로 필요로하므로 물리적 코어 * 2 아파치 2 프로세스보다 더 많은 것은 없다고 생각합니다 ....
— BarsMonster

이것을 확인했습니다. Apache2 프로세스 수를 4에서 16으로 늘려도 성능이 전혀 향상되지는 않았습니다 (0.5 % 감소). nginx 작업자 수를 2 또는 4로 늘려도 아무런 개선이 없었습니다.
— BarsMonster

1
데이터가 상당히 정적 인 경우 (즉, 다른 모든 페이지로드를 업데이트하지 않는 경우) query_cache를 늘릴 수 있습니다. MySQL은 그런 식으로 결과 집합을 잡고 메모리에서 가져옵니다. 그러나 캐시되는 테이블이 해당 시간 동안 쓰기를 수신하면 데이터에 영향을 미치지 않더라도 캐시를 무효화하여 메모리를 낭비합니다.
— Erik Giberti

지금은 100 % 쿼리 캐시 적중률을보고, MySQL은 ... 느린 여전히 느낌
— BarsMonster

1
MySQL 구성 파일에 skip-name-resolve를 추가하십시오. 그러면 서버에 대한 모든 연결에서 DNS 조회가 저장됩니다. 여기서 단점은 모든 연결을 IP로 잠 가야한다는 것입니다 ( '%'를 사용하지 않는 경우). SQL이 동일한 서버에 있고 localhost 이외의 다른 곳에 액세스 할 필요가없는 경우 skip-networking을 추가하여 전체 TCP / IP 스택을 종료 할 수도 있습니다. 그러나 병목 현상은 Apache라고 생각합니다.
— Erik Giberti

0

이 하중은 공구 또는 실제 하중에 의해 생성됩니까?

memcached를 확인하고 싶을 수도 있습니다. 높은 연결 속도에서 문제가 발생하여 응용 프로그램에서 대기 시간이 발생했습니다.

로드 생성기를 사용하는 경우 작은 정적 페이지를 칠 때 얻는 결과는 무엇입니까?

로드하는 동안 TIME_WAIT 조건에 대한 네트워크 스택을 점검 할 수 있습니다. 아마도 연결 대기열을 채우고있을 것입니다.

당신이 볼 수있는 약 100 가지 이유와 항목이 있지만 더 많은 정보가 없다면, 나는이 시점에서 추측을 던지고 있습니다.


서버 자체에서 벤치마킹하는 ab-c 10 -t 10 URL을 통해 테스트되었으므로 네트워크에 문제가 없어야합니다. 귀하의 요청에 따라 더 많은 벤치 마크를 게시했습니다.
— BarsMonster

나는 ab로 튜닝하는 데 너무 많은 노력을 기울이지 않을 것입니다. 실제 성능으로 제대로 변환되지 않을 수도 있습니다. 당신이하고 싶은 일은 앱을 분석하고 각 구성 요소를 테스트하는 것입니다. 예를 들어 아주 작은 정적 페이지만으로 아파치 서버를 직접 누르십시오. 백엔드에서 최대 요청 / 초에 대한 정보를 얻을 수 있습니다. nginx를 앞에두고 동일한 백엔드 파일을 호출하여 다시 테스트하십시오. 그런 다음 간단한 "hello world"유형의 PHP 페이지로 테스트하십시오. 때로는 모든 레이어가 간단한 것을 숨길 수 있습니다. 또한 테스트 중에 연결을 확인하십시오. 네트워크 스택이 가득 차지 않았는지 확인하십시오.
— jeffatrackaid

어제 이러한 벤치 마크를 수행했으며 업데이트 된 원래 질문 설명에 있습니다. 또한 로컬 호스트에서 테스트를 수행하므로 네트워크에 문제가 없습니다.
— BarsMonster

로컬 호스트에서 수행하더라도 네트워크가 문제가 될 수 있습니다. 귀하의 경우는 아니지만 문제가 발생할 수 있습니다. 적어도 현재 PHP 설정에서 ~ 450 req / sec의 상한선이 없습니다. 다음 단계는 데이터베이스 호출을 삭제하고 변경 내용을 확인하는 것입니다. 높은 수준의 튜닝을 수행 할 때이 문제를 해결하는 것이 가장 문제가되는 레이어를 정확히 찾아내는 데 도움이 될 수 있습니다.
— jeffatrackaid

-1

이와 같은 시간 문제의 99 %%가 데이터베이스로 추적됩니다. 타격 지수를 먼저 확인하십시오. 그래도 작동하지 않으면 가능한 모든 것을 캐싱하기 시작하십시오.


모든 인덱스이며 제가 말한 것처럼 100 % 사례에서 MySQL 쿼리 캐시에 도달합니다
— BarsMonster

-1

가능한 경우 연결 풀러를 사용하여 데이터베이스를 웹 응용 프로그램에 연결 한 상태로 유지하십시오 (각 요청에서 다시 연결하지 않아도 됨). 그것은 속도의 큰 차이를 만들 수 있습니다.

또한 EXPLAIN을 사용하여 모든 쿼리를 분석하십시오 (또한 SHOW PROFILE?


모든 쿼리는 인덱스를 사용합니다. MySQL 연결 풀이 사용됩니다.
— BarsMonster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.