나는 약 1 주일 동안 다양한 접근 방식을 사용하여 테스트를하는 동안 엄청난 부하로 인해 서버를 항상 "충돌"시킵니다.간단한 업데이트/선택 (큰 테이블, 많은 연결)에서 Mysql 테이블 성능 문제가 발생했습니다.
mysql> explain select id FROM task_jobs FORCE INDEX (index_update_get_work) WHERE customer_job_id=31 AND client_reserved=0 AND result_delivered=0 AND (assigned_instance is NULL) LIMIT 10;
+----+-------------+--------------------+------------+------+-----------------------+-----------------------+---------+-------------------------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------------+------------+------+-----------------------+-----------------------+---------+-------------------------+--------+----------+-------------+
| 1 | SIMPLE | task_jobs | NULL | ref | index_update_get_work | index_update_get_work | 14 | const,const,const,const | 104226 | 100.00 | Using where |
+----+-------------+--------------------+------------+------+-----------------------+-----------------------+---------+-------------------------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
일반형 유지 : 수백만 개의 행이 늘어나는 테이블이 있습니다.
이 테이블은 한 번에 수천 개의 클라우드 인스턴스에 수백 개의 작업 작업을 제공합니다.
모든 인스턴스는 내 테이블 (최대 3000 개의 쿼리)을 쿼리하고 작업 데이터를 수신하도록 요청합니다.
"열린 작업"으로 수십만 개의 행이 있지만 한 번에 한 인스턴스에 10-20 개만 전달됩니다. 가장 성능이 좋은 여전히 큰 문제
내 현재의 접근 방식 :
내가 LIMIT 10 행에 UPDATE을 WHERE customer_job_id = 31 AND (NULL assigned_instance이) = 0을 client_reserved AND = 0 result_delivered AND
쿼리가 자체 설명하는 것 같아요, 아직 특정 "작업 ID"에서 결과를 배달하지 않은 할당되지 않은 작업을 찾습니다.
쿼리는 SELECT 대신 UPDATE로 시작됩니다.이제 프로그램 로직은 delievered 행의 일부를 선택하고 마지막으로 (X, X, X,을,을 X X)
내가 IN WHERE ID를 사용하여 인스턴스에 할당하는 두 번째 업데이트한다 이 접근 방식을 사용하여 바쁜 것으로 업데이트하여 10 개의 행을 빠르게 "잠글"수 있으므로 다음 인스턴스도 다른 10 개의 행을 잠글 수 있습니다.
이 작업은 정상적으로 작동하며 한 번에 100 개 인스턴스에 문제가 없는데 500 개 인스턴스로로드하면 서버가 잠긴다. 데이터베이스 연결을 수백 개의 LOCK 된 요청으로 채워서 15 초 (최적화 전 140 초) 동안 10 개의 행을 업데이트합니다.
처음에 볼 수 있듯이 SELECT (실제로는 UPDATE SET client_reserved = 1, assigned_instance = $ instance_id)는 100k 행 (아마도 그 이상)을 거쳐야합니다. 단지 10 명이 선택 되더라도 작업이 완료되고 처음 10 개가 업데이트되기 전에 모든 작업을 검토하는 것으로 보입니다. 적어도 EXPLAIN이이를 알려줍니다.
기본적으로 제 질문은 더 나은 접근 방법을 찾는 것입니다.
나는 수천 개의 별개의 연결로부터 수초 내에 수천 개의 행을 채울 필요가있다.
"customer_job_id = 31 AND client_reserved = 0 AND result_delivered = 0 AND (assigned_instance가 NULL)"인 100-500k 개의 작업/행 중 적은 수의 행을 가져올 필요가있을 때마다.
assigned_instance는 varchar (NULL 조건의 경우 인덱스가 1)이며 나머지는 tinyint (1)입니다. 나는 그들 모두를 결합하는 색인을 만들었지 만 그것은 정말로 도움이되지 못했다.
업데이트에 대한 명확한
: 다른 명의 동시 요청이있는 경우 주 서버의 API가 모르기 때문에 나는 "UPDATE"를 사용하고
"나에게 일을주는".
그래서 현재 인스턴스에 대해 "예약"하기 위해 여러 행에서 UPDATE를 사용했습니다.
UPDATE가 SQL의 "원자"연산이므로 다른 요청이 동일한 작업 (경쟁 조건)과 함께 제공 될 위험이 없습니다.
업데이트 질문
일반적인 질문 : 나는 10이 충분히있는 경우 10
은 왜 10 만 개 결과를 검색 않습니다 LIMIT 사용할 수 있습니까? ORDER BY RAND()를 추가하면 실제로 100k 결과를 모두 살펴보고 순서를 바꾸어야합니다 (동일한 성능 비용). mysql이 10 개의 히트를 발견하면 멈추지 않는 이유는 무엇입니까? (LIMIT 10과 ORDER BY 절을 생략하여 바랬습니다)
LIMIT에 대한 일반적인 질문 : MySQL은 쿼리를 만족하는 모든 행을 검색 한 다음 OFFSET에서 시작하여 LIMIT 개의 행을 제외한 모든 것을 버립니다. 왜 더 잘 설계되지 않았습니까?그것은 불가능하기 때문에, MySQL은 행을 수집하고, 순서를 적용해야하며 (이 시점에서 쿼리를 만족시키는 모든 것을 수집했다.) 주문이 적용된 후에 만, 처음부터 시작하여 10 개의 행을 반환 할 수있다. LIMIT는 성능을 향상시키지 않고 단순히 소켓에 기록 된 레코드 수를 줄입니다. –
@ N.B. 나는 주문할 때 이해하지만 모든 주문을 빠뜨리려고했습니다. 그런 경우에는 mysql이 순서를 무시하고 일단 LIMIT가 실행되면 작업을 중지 할 수 있습니다. 아니 ? – John
아닙니다. order by 절을 지정하지 않더라도 반환 레코드를 찾아야하는 순서가 있습니다. 이 주문이 기본 키 (디스크에 기록 된 방식) 인 경우 10 개의 레코드 및 정지 만 찾을 수 있습니다. 실제로 그렇게 할 수 있는지 확신 할 수 없기 때문에 (나는 그런 것을 개발하는 것이 중요 함을 알고 있습니다.) 실제로 그것을하고 있다고 주장하지는 않을 것입니다. 그러나 저는 여러분이 어디에서 왔는지 알고 있습니다. 우리는 모두 그런 식으로 행동하는 경우에 그것을 좋아하십시오 :) –