MySQL 에서 제출 되 지 않 은 SQL 인 스 턴 스 를 찾 습 니 다.

18729 단어 mysql미 제출사무.
오래 전에 블 로그'MySQL 은 제출 되 지 않 은 트 랜 잭 션 정 보 를 어떻게 찾 습 니까?'를 정리 한 적 이 있 는데 지금 보면 이 글 에서 많은 지식 이나 관점 이 약간 천박 하거나 깊이 가 없다 거나 심지어 일부 결론 은 잘못된 것 이다.다음은 이 화 제 를 다시 한번 토론 해 보 겠 습 니 다.그럼 예전 의 예 로 소개 하 겠 습 니 다.
--테스트 환경 데이터 준비(실험 환경 은 MySQL 8.0.18 커 뮤 니 티 버 전)

mysql> create table kkk(id int , name varchar(12));
Query OK, 0 rows affected (0.34 sec)
 
mysql> insert into kkk values(1, 'kerry');
Query OK, 1 row affected (0.01 sec)
 
mysql> insert into kkk values(2, 'jerry');
Query OK, 1 row affected (0.00 sec)
 
mysql> insert into kkk values(3, 'ken');
Query OK, 1 row affected (0.00 sec)
 
mysql> 
 
mysql> create table t(a varchar(10));
Query OK, 0 rows affected (0.47 sec)
 
mysql> insert into t values('test');
Query OK, 1 row affected (0.00 sec)
세 션 창(ID=38 연결)에서 다음 SQL 을 실행 합 니 다.

mysql> select connection_id() from dual;
+-----------------+
| connection_id() |
+-----------------+
|  38 |
+-----------------+
1 row in set (0.00 sec)
 
mysql> set session autocommit=0;
Query OK, 0 rows affected (0.00 sec)
 
mysql> delete from kkk where id =1;
Query OK, 1 row affected (0.00 sec)
 
mysql> 
다음 세 션 창(ID=39 연결)에서 SQL 실행

mysql> SELECT t.trx_mysql_thread_id
 -> ,t.trx_id
 -> ,t.trx_state
 -> ,t.trx_tables_in_use
 -> ,t.trx_tables_locked
 -> ,t.trx_query
 -> ,t.trx_rows_locked 
 -> ,t.trx_rows_modified
 -> ,t.trx_lock_structs
 -> ,t.trx_started
 -> ,t.trx_isolation_level
 -> ,p.time 
 -> ,p.user
 -> ,p.host
 -> ,p.db
 -> ,p.command
 -> FROM information_schema.innodb_trx t 
 -> INNER JOIN information_schema.processlist p 
 ->  ON t.trx_mysql_thread_id = p.id 
 -> WHERE t.trx_state = 'RUNNING' 
 -> AND p.time > 4 
 -> AND p.command = 'Sleep'\G 
*************************** 1. row ***************************
trx_mysql_thread_id: 38
  trx_id: 7981581
  trx_state: RUNNING
 trx_tables_in_use: 0
 trx_tables_locked: 1
  trx_query: NULL
 trx_rows_locked: 4
 trx_rows_modified: 1
 trx_lock_structs: 2
 trx_started: 2020-12-03 15:39:37
trx_isolation_level: REPEATABLE READ
  time: 23
  user: root
  host: localhost
   db: MyDB
  command: Sleep
1 row in set (0.00 sec)
위의 SQL 에서 실 행 된 SQL 을 찾 을 수 없 지만,사실은 MySQL 에서 제출 되 지 않 은 마지막 SQL 은 아래 스 크 립 트 를 통 해 정확하게 찾 을 수 있 습 니 다.다음 과 같다.

SELECT t.trx_mysql_thread_id   AS connection_id
 ,t.trx_id     AS trx_id  
 ,t.trx_state     AS trx_state 
 ,t.trx_started    AS trx_started 
 ,TIMESTAMPDIFF(SECOND,t.trx_started, now()) AS "trx_run_time(s)" 
 ,t.trx_requested_lock_id   AS trx_requested_lock_id
 ,t.trx_operation_state   AS trx_operation_state
 ,t.trx_tables_in_use    AS trx_tables_in_use
 ,t.trx_tables_locked    AS trx_tables_locked
 ,t.trx_rows_locked    AS trx_rows_locked
 ,t.trx_isolation_level   AS trx_isolation_level
 ,t.trx_is_read_only    AS trx_is_read_only
 ,t.trx_autocommit_non_locking   AS trx_autocommit_non_locking
 ,e.event_name     AS event_name
 ,e.timer_wait / 1000000000000   AS timer_wait
 ,e.sql_text 
FROM information_schema.innodb_trx t, 
 performance_schema.events_statements_current e, 
 performance_schema.threads c 
WHERE t.trx_mysql_thread_id = c.processlist_id 
 AND e.thread_id = c.thread_id\G;
다음 캡 처 에서 보 듯 이:

세 션 창(ID=38 연결)에서 다음 SQL 을 계속 실행 합 니 다."select*from t;"아래 와 같다

mysql> set session autocommit=0;
Query OK, 0 rows affected (0.01 sec)
 
mysql> delete from kkk where id =1;
Query OK, 1 row affected (0.00 sec)
 
mysql> select * from t;
+------+
| a |
+------+
| test |
+------+
1 row in set (0.00 sec)
 
mysql> 
세 션 창(연결 ID=39)에서 다음 SQL 을 계속 실행 하면 마지막 으로 실 행 된 SQL 문 구 를 캡 처 할 수 있 습 니 다."select*from t"

mysql> SELECT t.trx_mysql_thread_id   AS connection_id
 -> ,t.trx_id     AS trx_id  
 -> ,t.trx_state     AS trx_state 
 -> ,t.trx_started    AS trx_started 
 -> ,TIMESTAMPDIFF(SECOND,t.trx_started, now()) AS "trx_run_time(s)" 
 -> ,t.trx_requested_lock_id   AS trx_requested_lock_id
 -> ,t.trx_operation_state   AS trx_operation_state
 -> ,t.trx_tables_in_use    AS trx_tables_in_use
 -> ,t.trx_tables_locked    AS trx_tables_locked
 -> ,t.trx_rows_locked    AS trx_rows_locked
 -> ,t.trx_isolation_level   AS trx_isolation_level
 -> ,t.trx_is_read_only    AS trx_is_read_only
 -> ,t.trx_autocommit_non_locking   AS trx_autocommit_non_locking
 -> ,e.event_name     AS event_name
 -> ,e.timer_wait / 1000000000000   AS timer_wait
 -> ,e.sql_text 
 -> FROM information_schema.innodb_trx t, 
 -> performance_schema.events_statements_current e, 
 -> performance_schema.threads c 
 -> WHERE t.trx_mysql_thread_id = c.processlist_id 
 -> AND e.thread_id = c.thread_id\G; 
*************************** 1. row ***************************
  connection_id: 38
   trx_id: 7981581
   trx_state: RUNNING
  trx_started: 2020-12-03 15:39:37
  trx_run_time(s): 237
 trx_requested_lock_id: NULL
 trx_operation_state: NULL
  trx_tables_in_use: 0
  trx_tables_locked: 1
  trx_rows_locked: 4
 trx_isolation_level: REPEATABLE READ
  trx_is_read_only: 0
trx_autocommit_non_locking: 0
  event_name: statement/sql/select
  timer_wait: 0.0002
   sql_text: select * from t
1 row in set (0.00 sec)
 
ERROR: 
No query specified

또한 위의 SQL 은 제출 되 지 않 은 업무 가 마지막 으로 실 행 된 SQL 문 구 를 가 져 올 수 있 습 니 다.생산 환경 에서 하나의 업무 에서 하나의 SQL 문 구 를 가 져 올 수 있 는 것 이 아니 라 여러 개의 SQL 문장의 집합 입 니 다.제출 되 지 않 은 트 랜 잭 션 에서 실 행 된 모든 SQL 을 찾 으 려 면 어떻게 합 니까?사실 MySQL 에는 방법 이 있 습 니 다.다음 SQL 문 구 를 찾 거나

SELECT trx.trx_mysql_thread_id AS processlist_id
 ,sc.thread_id
 ,trx.trx_started
 ,TO_SECONDS(now())-TO_SECONDS(trx_started) AS trx_last_time 
 ,pc1.user
 ,pc1.host
 ,pc1.db
 ,sc.SQL_TEXT AS current_sql_text
 ,sh.history_sql_test
FROM INFORMATION_SCHEMA.INNODB_TRX trx
INNER JOIN INFORMATION_SCHEMA.processlist pc1 ON trx.trx_mysql_thread_id=pc1.id
INNER JOIN performance_schema.threads th on th.processlist_id = trx.trx_mysql_thread_id
INNER JOIN performance_schema.events_statements_current sc ON sc.THREAD_ID = th.THREAD_ID
INNER JOIN (
  SELECT thread_id AS thread_id, GROUP_CONCAT(SQL_TEXT SEPARATOR ';') AS history_sql_test
  FROM performance_schema.events_statements_history 
  GROUP BY thread_id 
  ) sh ON sh.thread_id = th.thread_id
WHERE trx_mysql_thread_id != connection_id()
 AND TO_SECONDS(now())-TO_SECONDS(trx_started) >= 0 ;
그러나 이 두 SQL 은 문제 가 있 습 니 다.현재 연결 역사상 실 행 된 모든 SQL 을 찾 을 수 있 습 니 다.(물론 전 제 는 이 SQL 들 이 performance 에 저장 되 어 있다 는 것 입 니 다.schema.events_statements_history 표 에서),즉 이 SQL 은 제출 되 지 않 은 사무소 에서 실 행 된 스 크 립 트 를 조회 할 뿐만 아니 라 과거 SQL 스 크 립 트 도 많이 조회 할 수 있 습 니 다.예 를 들 어 이 세 션(연결)이전 업무 의 SQL 문 구 는 비교적 골 치 아 픈 문제 도 있 습 니 다.어떤 SQL 이 어떤 업무 에 대응 하 는 지 구분 하기 어렵 습 니 다.다른 정 보 를 빌려 선별 해 야 한다.시간 과 힘 이 비교적 많이 든다.다음 과 같이 캡 처 하여 보 여 줍 니 다.

왜냐하면 informationschema.innodb_trx 시스템 테이블 에 업무 시작 시간 포함(trxstarted),다른 시스템 표 는 업무 와 관련 된 시간 이 없 으 며,permance 를 빌 릴 수 밖 에 없습니다.schema.events_statements_history 의 TIMERSTART 필드 에서 이벤트 의 SQL 이 실행 되 기 시작 하 는 시간 을 가 져 옵 니 다.이 시간 은 반드시 업무 의 시작 시간 보다 작 거나 같 을 것 입 니 다(trxstarted 시작 하 다그래서 이 돌파구 에서 제출 되 지 않 은 모든 SQL 을 찾 아 보 세 요.다음은 TIMERSTART 등의 필드 에 대한 상세 한 소개.
TIMER 에 대하 여START,TIMER_END,TIMER_WAIT 의 소 개 는 다음 과 같다.
TIMER_START,TIMER_END,TIMER_WAIT:사건 의 시간 정보.이 값 의 단 위 는 피 초(조 분 의 1 초)다.
TIMER_START 와 TIMEREND 값 은 이벤트 의 시작 시간 과 종료 시간 을 나 타 냅 니 다.
TIMER_WAIT 는 이벤트 수행 에 소요 되 는 시간(지속 시간)입 니 다.
이벤트 가 완료 되 지 않 으 면 TIMEREND 는 현재 시간,TIMERWAIT 가 지금까지 거 친 시간(TIMEREND - TIMER_START)。
감시 기기 배치 표 setupinstruments 에 대응 하 는 모니터 TIMED 필드 가 NO 로 설정 되 어 있 으 면 이 모니터 의 시간 정 보 를 수집 하지 않 습 니 다.그러면 이 사건 에 대해 수집 한 정보 기록 에 TIMERSTART,TIMER_END 와 TIMERWAIT 필드 값 은 모두 NULL 입 니 다.
테스트,오 랜 고생 끝 에 거의 완벽 한 SQL 을 만 들 었 습 니 다.

SELECT @dt_ts:=UNIX_TIMESTAMP(NOW());
SELECT 
 @dt_timer:=MAX(sh.TIMER_START)
FROM performance_schema.threads AS t
INNER JOIN performance_schema.events_statements_history AS sh
ON t.`THREAD_ID`=sh.`THREAD_ID`
WHERE t.PROCESSLIST_ID=CONNECTION_ID();
 
 
SELECT sh.current_schema  AS database_name
 ,t.thread_id
 ,it.trx_mysql_thread_id  AS connection_id
 ,it.trx_id
 ,sh.event_id
 ,it.trx_state
 ,REPLACE(REPLACE(REPLACE(sh.`SQL_TEXT`,'
',' '),'\r',' '),'\t',' ') AS executed_sql ,it.trx_started ,FROM_UNIXTIME(@dt_ts-CAST((@dt_timer-sh.TIMER_START)/1000000000000 AS SIGNED)) AS start_time ,FROM_UNIXTIME(@dt_ts-CAST((@dt_timer-sh.TIMER_END) /1000000000000 AS SIGNED)) AS end_time ,(sh.TIMER_END-sh.TIMER_START)/1000000000000 AS used_seconds ,sh.TIMER_WAIT/1000000000000 AS wait_seconds ,sh.LOCK_TIME/1000000000000 AS lock_seconds ,sh.ROWS_AFFECTED AS affected_rows ,sh.ROWS_SENT AS send_rows FROM performance_schema.threads AS t INNER JOIN information_schema.innodb_trx it ON it.trx_mysql_thread_id = t.processlist_id INNER JOIN performance_schema.events_statements_history AS sh ON t.`THREAD_ID`=sh.`THREAD_ID` WHERE t.PROCESSLIST_ID IN ( SELECT p.ID AS conn_id FROM `information_schema`.`INNODB_TRX` t INNER JOIN `information_schema`.`PROCESSLIST` p ON t.trx_mysql_thread_id=p.id WHERE t.trx_state='RUNNING' AND p.COMMAND='Sleep' AND p.TIME>2 ) AND sh.TIMER_START<@dt_timer AND FROM_UNIXTIME(@dt_ts-CAST((@dt_timer-sh.TIMER_START)/1000000000000 AS SIGNED)) >=it.trx_started ORDER BY it.trx_id ASC, sh.TIMER_START ASC;

제출 되 지 않 은 SQL 을 찾 을 수 있 습 니 다.간단 한 테스트 는 전혀 문제 가 없 으 며 제출 되 지 않 은 몇 개의 테스트 를 구성 하 는 것 도 OK 입 니 다.그러나 위의 SQL 은 세 개의 SQL 로 구성 되 어 있어 서 좀 어색 하 다.연 구 를 해 보 니 아래 SQL 로 개조 할 수 있다.

SELECT sh.current_schema  AS database_name
 ,t.thread_id
 ,it.trx_mysql_thread_id AS connection_id
 ,it.trx_id
 ,sh.event_id
 ,it.trx_state
 ,REPLACE(REPLACE(REPLACE(sh.`SQL_TEXT`,'
',' '),'\r',' '),'\t',' ') AS executed_sql ,it.trx_started ,DATE_SUB(NOW(), INTERVAL (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='UPTIME') - sh.TIMER_START*10e-13 second) AS start_time ,DATE_SUB(NOW(), INTERVAL (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='UPTIME') - sh.TIMER_END*10e-13 second) AS end_time ,(sh.TIMER_END-sh.TIMER_START)/1000000000000 AS used_seconds ,sh.TIMER_WAIT/1000000000000 AS wait_seconds ,sh.LOCK_TIME/1000000000000 AS lock_seconds ,sh.ROWS_AFFECTED AS affected_rows ,sh.ROWS_SENT AS send_rows FROM performance_schema.threads AS t INNER JOIN information_schema.innodb_trx it ON it.trx_mysql_thread_id = t.processlist_id INNER JOIN performance_schema.events_statements_history AS sh ON t.`THREAD_ID`=sh.`THREAD_ID` WHERE t.PROCESSLIST_ID IN ( SELECT p.ID AS conn_id FROM `information_schema`.`INNODB_TRX` t INNER JOIN `information_schema`.`PROCESSLIST` p ON t.trx_mysql_thread_id=p.id WHERE t.trx_state='RUNNING' AND p.COMMAND='Sleep' AND p.TIME>2 ) AND sh.TIMER_START<(SELECT VARIABLE_VALUE*1000000000000 FROM performance_schema.global_status WHERE VARIABLE_NAME='UPTIME') AND DATE_SUB(NOW(), INTERVAL (SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME='UPTIME') - sh.TIMER_START*10e-13 second) >=it.trx_started ORDER BY it.trx_id ASC, sh.TIMER_START ASC;
주의:performanceschema.global_status 는 MySQL 5.7 로 도입 되 었 습 니 다.데이터베이스 가 MySQL 5.6 이면 INFORMATION 을 사용 할 수 있 습 니 다.SCHEMA.GLOBAL_STATUS SQL 의 performance 바 꾸 기schema.global_status
그렇다면 이 SQL 은 완벽 할 까?네티즌 MSSQL 123 은 테스트 환경 에서 상기 SQL 에서 아무런 데이터 도 찾 을 수 없다 는 것 을 발견 했다.왜냐하면 FROMUNIXTIME(@dt_ts-CAST((@dt_timer-sh.TIMER_START)/1000000000000 AS SIGNED)) >=it.trx_started 는 데 이 터 를 걸 러 내 고 해당 하 는 trx 를 검사 합 니 다.started 값 이 start 보다 큽 니 다.time

-----------------------------------------------------------------------------------
그러면 같은 테스트 환경,격일 테스트 를 할 때 갑자기 위의 첫 번 째 SQL 이 정상 이 고 두 번 째 SQL 은 서로 다른 쓰기 로 인해 starttime 에 미세한 차이 가 있어 조회 결과 가 완전히 다르다(두 번 째 SQL 문 구 는 밀리초 까지 정확 하고 비교 할 때 편차 로 일부 데 이 터 를 걸 러 낸다)


------------------------------------------------------------------------------------------------------------------------------------------------
관련 문 서 를 검색 하여 TIMER 알 아 보기START 필드 값 에 변동 이나 편차 가 있 을 수 있 습 니 다.그러면 이 파동 이나 편차 가 조회 결과 에 영향 을 줄 수 있 습 니 다.아래 내용 은http://porthos.ist.utl.pt/docs/mySQL/performance-schema.html
Modifications to the setup_timers table affect monitoring immediately. Events already in progress may use the original timer for the begin time and the new timer for the end time. To avoid unpredictable results after you make timer changes, use TRUNCATE TABLE to reset Performance Schema statistics.
The timer baseline (“time zero”) occurs at Performance Schema initialization during server startup. TIMER_START and TIMER_END values in events represent picoseconds since the baseline. TIMER_WAIT values are durations in picoseconds.
Picosecond values in events are approximate. Their accuracy is subject to the usual forms of error associated with conversion from one unit to another. If the CYCLE timer is used and the processor rate varies, there might be drift. For these reasons, it is not reasonable to look at the TIMER_START value for an event as an accurate measure of time elapsed since server startup. On the other hand, it is reasonable to use TIMER_START or TIMER_WAIT values in ORDER BY clauses to order events by start time or duration.
The choice of picoseconds in events rather than a value such as microseconds has a performance basis. One implementation goal was to show results in a uniform time unit, regardless of the timer. In an ideal world this time unit would look like a wall-clock unit and be reasonably precise; in other words, microseconds. But to convert cycles or nanoseconds to microseconds, it would be necessary to perform a division for every instrumentation. Division is expensive on many platforms. Multiplication is not expensive, so that is what is used. Therefore, the time unit is an integer multiple of the highest possible TIMER_FREQUENCY value, using a multiplier large enough to ensure that there is no major precision loss. The result is that the time unit is “picoseconds.” This precision is spurious, but the decision enables overhead to be minimized.
Before MySQL 5.7.8, while a wait, stage, statement, or transaction event is executing, the respective current-event tables display the event with TIMER_START populated, but with TIMER_END and TIMER_WAIT set to NULL
그 중의 한 단락 의 내용 은 다음 과 같다.
이벤트 의 피 초 값 은 근사치 입 니 다.그들의 정확성 은 한 단위 에서 다른 단위 로 전환 하 는 것 과 관련 된 흔 한 오차 형식의 영향 을 받는다.CYCLE 타 이 머 를 사용 하고 프로세서 속도 가 바 뀌 면 편차 가 있 을 수 있 습 니 다.이러한 이유 로 사건 을 TIMERSTART 값 을 서버 가 시 작 된 이래 거 친 시간의 정확 한 도량 으로 보 는 것 은 불합리 하 다.한편,ORDER BY 자구 에 TIMER 을 사용 합 니 다.START 또는 TIMERWAIT 값 은 시작 시간 이나 지속 시간 에 따라 사건 을 정렬 하 는 것 이 합 리 적 입 니 다.
우 리 는 흔히 일 격 필 살 문 제 를 해결 하려 고 하지만 복잡 한 환경 과 통제 할 수 없 는 요소 로 인해 현실 은 흔히'은 탄 이 없다'는 잔혹 함 이다.TIMER 를 만나면START 의 파동 이나 편차 가 조회 결과 에 영향 을 미 칠 때 우 리 는 글 앞의 SQL 을 통 해 대량의 SQL 을 찾 은 다음 에 다른 필드 나 정 보 를 통 해 어떤 것 이 제출 되 지 않 은 SQL 인지 인공 적 으로 선별 해 야 한다.
참고 자료:
https://stackoverflow.com/questions/25607249/mysql-performance-schema-how-to-get-event-time-from-events-statements-current-ta
http://porthos.ist.utl.pt/docs/mySQL/performance-schema.html
https://dev.mysql.com/doc/refman/5.7/en/performance-schema-timing.html
https://dev.mysql.com/doc/refman/8.0/en/performance-schema-timing.html
여기에 MySQL 이 제출 되 지 않 은 사무 SQL 을 찾 아 냈 다 는 글 이 소개 되 었 습 니 다.더 많은 MySQL 이 제출 되 지 않 은 사무 SQL 내용 을 찾 아 냈 습 니 다.이전 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기