MySQL 서버는 프로세스 기반이 아니라 스레드 기반으로 작동하며,크게 포그라운드 스레드와 백그라운드 스레드로 구분할 수 있습니다.
포그라운드 스레드(클라이언트 스레드)
포그라운드 스레드는 최소한 MySQL 서버에 접속된 클라이언트의 수만큼 존재하며, 주로 각 클라이언트 사용자가 요청하는 쿼리 문장을 처리합니다. 클라이언트 사용자가 작업을 마치고 커넥션을 종료하면 해당 커넥션을 담당하던 쓰레드는 다시 스레드 캐시로 되돌아갑니다. 이때 이미 스레드 캐시에 일정 개수 이상의 대기중인 스레드가 있으면 스레드 캐시에 넣지 않고 스레드를 종료시켜 일정 개수의 스레드만 스레드 캐시에 존재하게 합니다.이때 스레드 캐시에 유지할 수 있는 최대 스레드 개수는 thead_cashe_size 시스템 변수로 설정합니다.
포그라운드 스레드는 데이터을 MySQL의 데이터 버퍼나 캐시로부터 가져오며, 버퍼나 캐시에 없는경우에는 직접 디스크의 데이터나 인덱스 파일로부터 데이터를 읽어와서 작업을 처리한합니다. MyISAM 테이블은 디스크 쓰기 작업까지 포그라운드 스레드가 처리하지만(MyISAM도 지연된 쓰기가 있지만 일반적인 방식은 아님.)InnoDB 테이블은 데이터 버퍼나 캐시까지만 포그라운드 스레드가 처리하고, 나머지 버퍼로부터 디스크까지 기록하는 작업은 백그라운드 스레드가 처리합니다.
백그라운드 스레드
MyISAM의 경우에는 별로 해당 사항이 없는 부분이지만 InnoDB는 다음과 같이 여러가지 작업이 백그라운드로 처리됩니다.
-인서트 버퍼를 병합하는 스레드
-로그롤 디스크로 기록하는 스레드
-InnoDB 버퍼 풀의 데이터를 디스크에 기록하는 스레드
-데이터를 버퍼로 읽어 오는 스레드
-잠금이나 데트락을 모니터링하는 스레드
모두 중요한 역할을 하지만 그중에서도 가장 중요한 것은 로그 스레드와 버퍼의 데이터를 디스크로 내려쓰는 작업을 처리하는 쓰기 쓰레드일 것입니다.MySQL 5.5 버전부터 데이터 쓰기 스레드와 데이터 읽기 스레드를 개수를 2개 이상 지정할 수 있게 됐으며, innodb_writer_io_threads와 inno_read_io_threads 시스템 변수로 스레드의 개수를 설정합니다. InnoDB에서도 데이터를 데이터를 읽은 작업을 주로 클라이언트 스레드에서 처리되기 때문에 읽기 쓰레드는 많이 설정할 필요가 없지만 2~4정도, DAS나 SAN과 같은 스토리지를 사용할 때는 디스크를 최적으로 사용할 수 있을 만큼 충분히 설정하는 것이 좋습니다.
사용자의 요청을 처리하는 도중 데이터의 쓰기 작업은 지연(버퍼링)되어 처리될 수 있지만 데이터의 읽기 작업은 절대 지연(사용자가 SELLECT 쿼리를 실행했는데 "요정된 SELLECT는 10뒤에 결과를 돌려주겠다"라고 응답을 보내는 DBMS는 없다.)될 수 없다. 그래서 일반적인 상용 DBMS에는 대부분 쓰기 작업을 버퍼링해서 일괄처리하는 기능이 탑재돼 있으며, InnoDB 또한 이러한 방식으로 처리한다. 하지만 MyISAM은 그렇지 않고 사용자 스레드가 쓰기 작업까지 함께 처리하도록 설계되어 있습니다. 이러한 이유로 InnoDB에서는 UPDATE, INSERT, DELETE 쿼리로 데이터가 변경되는 경우 데이터가 디스크 데이터 파일로 완전히 저장될 때까지 기다리지 않아도 됩니다. 하지만 MyISAM에서 일반적인 쿼리는 쓰기 버퍼링 기능을 사용할 수 없습니다.
전세계적으로 가장널리 사용되고 있는 오픈 소스 데이터베이스이며, MySQL AB사가 개발 배포/판매하고 있는 데이터베이스입니다.(현재는 오라클사가 인수)
표준 질이어인 SQL을 기본 데이터 언어로 사용고 서버의 디스크 드라이브에 있는 테이블에 데이터를 저장합니다.
다중사용자, 다중 쓰레드를 지원하고 유닉스. 리눅스, 윈도우 운영체제 등에서 사용할 수 있습니다.
MySQL은 웹사이트, 웹 앱플리케이션 및 모바일 앱용 데이터를 저장하는 데 사용됩니다. 개인, 교육 및 오픈 소스 웹사이에서 가장 많이 쓰입니다.
MySQL의 코드는 높은 수준의 트래픽을 처리할 수 있기 때문에 대규모 웹사이트 및 웹 애플리케이션에 널리사용 되며, 전체 텍스트 검색, 저장 프로시저, 트리거 및 보기와 같은 강력한 기능을 제공합니다. 또한 MySQL은 Community Edition과 Enterprise Edition의 두 가지 버전으로 제공됩니다. Community Edition은 무료이며 오픈 소스인 반면 Enterprise Edition은 더 많은 기능을 포함하고 있으며 상업적 사용이 허가되었습니다.
MySQL의 기능
-테이블에 데이터를 저장하고 SQL을 사용하여 이 데이터에 대한 작업을 수행 할 수 있는 관계형 데이터 베이스 관리 시스템
-트랜잭션 및 행 수준 잠금을 허용하는 InnoDB를 포함한 여러 스토리지 엔진을 지원
-저장 프로시저 및 트리거 지원과 같이 다른 RDBMS에서 찾을 수 없는 여러 기능을 제공
-크고 작은 웹사이트 모두에 전원을 공급하는 데 사용할 수 있다.
-GNU 일반 공중 사용 허가서에 따라 공개된 오픈 소스 소프트웨어는 무료로 사용할 수 있다.
MySQL의 장점
MySQL은 ACID 속성을 사용하여 여러 스토리지 엔진에서 트랜잭션 무결성을 제공합니다. 이를 통해 개발자는 기본 시스템 아키텍처 또는 스토리지 엔진에 대한 지식 없이도 SQL 인터페이스를 사용하여 확장성이 뛰어난 웹 기반 데이터베이스 애플리케이션을 쉽게 구축할 수 있습니다.
또한 MySQL에는 InnoDB 및 MyISAM을 비롯한 여러 스토리지 엔진을 사용하는 기능과 Memcached 캐싱 프로토콜에 대한 지원이 포함됩니다. 또한 복제를 지원하므로 개발자는 추가 개발 노력 없이 지리적으로 분산된 데이터베이스 시스템을 만들 수 있습니다.
마지막으로,MySQL은 크고 활동적인 사용자 커뮤니티를 보유하고 있습니다. 이는 풍부한 정보가 있음을 의미합니다.
가장 큰 이점은 경쟁력 즉 비용입니다. Oracle 또는 MS-SQL과 같은 다른 데이터 베이스와 비용차이가 심합니다.
메소드를 실행시킴과 동시에 결과 값이 기대되는 경우, 요청을 보낸쪽에서 결과가 반환될 때까지 기다린다.
안전성과 실행순서가 보장된다.
느리다.
비동기 (async)
요청을 보낸 쪽이 아니라 응답을 주는 쪽에서 결과를 알려준다.
요청을 보낸 쪽은 응답이 올 때까지 다른일을 하고 있는다.
빠르다.
순서가 보장되지 않기 때문에 처리하기가 까다로울 수 있다.
비동기로 처리하면 백그라운드에서 해당 작업을 처리하는 것을 의미한다.
블로킹(blocking)
스레드에서 발생하는 대기현상
네트워킹을 해야하는 대상이 여럿이라면 블로킹 소켓의 경우 대상 개수만큼 스레드를 생성한다. (멀티 스레드)
스레드가 많아지면 각 스레드가 차지하는 호출 스택 자원과 컨텍스트 스위칭 비용이 많이 발생한다.
즉, 블로킹 매커니즘의 주요 특징은 I/O가 가득 수신할 때까지 주어진 스레드가 아무것도 하지 않는다고 가정하는 것이다. 이 경우 메서드가 제어권을 반환하지 않으므로 애플리케이션 플로우가 블록된다.
논블로킹(non-blocking)
소켓 시스템콜에 대해 네트워크 시스템이 즉시 처리할 수 없는 경우라도 제어권이 바로 리턴되어 프로그램이 블록되지 않게하는 소켓 모드이다.
통신 상대가 여럿이거나 여러 작업을 병행하려면 논블로킹 또는 비동기 모드를 사용해야 한다.
어떤 시스템콜이 성공적으로 실행될 때까지 계속 루프를 돌면서 확인한다.(폴링)
단일 스레드로 운영할 경우 순차 실행을 보장할 수 있지만, 시간이 오래걸리면 그만 큼 다른 채널의 이벤트를 수행하지 못한다. 이럴땐 멀티스레드로 구현해야 한다.
논블로킹 매커니즘은 I/O요청을 즉시 큐에 넣고 애플리케이션 플로우 제어를 반환한다. 요청은 나중에 커널에서 처리된다. (리액티브)
블로킹보다 구현이 어렵지만, 성능과 확장에 유리하다.
논블로킹 ≠ 비동기
논블로킹 환경에서 응답이 빨리 돌아오지 않는다면 API는 에러와 함께 복귀하고 다른 행동을 하지 않는다. 반면 비동기 환경에서는 API는 항상 즉시 복귀하지만 응답은 늦게 돌아오기도 한다.
즉, 논블로킹 매커니즘에서 함수가 호출되어 있어도 제어권이 반환되었으므로 다음 작업을 실행하고, 비동기 매커니즘에서는 함수호출을 스택에 남겨두고 함수호출을 대신하여 작업이 계속 이어진다. (다른 스레드에서!)
비동기는 병렬(parallel)에 가깝고, 논블로킹은 폴링(polling)에 가깝다.
문제점
많은 요청이 들어왔을 때 순차적으로 실행이 돼 속도가 느리고 서버에 과부화 가 걸린다고 생각
해결방안
데이터가 많을 시 로직이 다 수행 되는 데 시간이 많이 걸리므로 api 요청을 즉시 반환하고 제어권이 반환되었으므로 다음 작업을 실행하고, 비동기 매커니즘에서는 함수 호출을 스택에 남겨두고 함수 호출을 대신하여 작업이 계속 이어지게 함.(다른 쓰레드에서)
@Configuration @EnableAsync public class AsyncConfig extends AsyncConfigurerSupport { // @Configuration : Spring설정 관련 Class로 @Component //등록되어 Scanning될 수 있다. // @EnableAsync : Spring method에서 비동기 기능을 사용가능하게 활성화 한다. // CorePoolSize :기본 실행 대기하는 Thread의 수** // MaxPoolSize :동시 동작하는 최대 Thread의 수 // QueueCapacity : MaxPoolSize초과 요청에서 Thread생성 요청시, //해당 요청을 Queue에 저장하는데 이때 최대 수용 가능한 Queue의 수, // Queue에 저장되어있다가 Thread에 자리가 생기면 하나씩 빠져나가 동작 // ThreadNamePrefix :생성되는 Thread접두사 지정 //크기가 제한된 큐에 작업이 가득 차면 집중 대응 정책 saturation policy가 동작한다. // 1.집중 대응 정책은 ThreadPoolExecutor에 setRejectedExecutionHandler메소드를 사용해 설정할 수 있다. //중단 정책(abort) //기본적으로 사용하는 집중 대응 정책이며 execute메소드에서 RejectedExecutionException을 던진다. @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor =newThreadPoolTaskExecutor(); executor.setCorePoolSize(8); executor.setMaxPoolSize(10); executor.setQueueCapacity(10000); executor.setThreadNamePrefix("async-tread-"); executor.setRejectedExecutionHandler(newThreadPoolExecutor.AbortPolicy()); executor.initialize(); return executor; } }
결과
응답 속도는 엄청 나게 빨라 졌지만 @Async의 기본 반환이 void라 프론트에 반환해줘야 할 데이터가 반환 되지 않음.
CompletableFuture<ResponseDto<LankRoundDto>> CompletableFuture을 사용하면 원하는 반환 값을 리턴 할 수 있다고 하여 사용
Java compiler는 .java 파일을 .class 라는 Java byte code로 변환.
컴파일
💡 여기서Java compiler는 JDK를 설치하면 bin 에 존재하는 javac.exe를 말한다. (즉, JDK에 Java compiler가 포함되어 있다는 소리이다.)
Byte Code 는 기계어가 아니기 때문에OS에서 바로 실행되지 않는 데 JVM은 OS가 ByteCode를 이해할 수 있도록 해석
터미널 에서 test.java 파일이 있는 디렉토리에서
명령어 : javac test.java 입력
실행하는 방법
.class 파일이 위치한 곳으로 이동 후 java <.class 파일 이름> 을 입력해 실행시킨다.
명령어 : java test 입력
바이트코드란 무엇인가
자바 바이트 코드(Java bytecode)란 자바 가상 머신이 이해할 수 있는 언어로 변환된 자바 소스 코드를 의미한다.
자바 컴파일러에 의해 변환되는 코드의 명령어 크기가 1바이트라서 자바 바이트 코드라고 불리고 있다.
이러한 자바 바이트 코드의 확장자는 .class이다.
자바 바이트 코드는 자바 가상 머신만 설치되어 있으면, 어떤 운영체제에서라도 실행될 수 있다.
JIT 컴파일러란 무엇이며 어떻게 동작하는가
JIT 컴파일(just-in-time compilation) 또는동적 번역(dynamic translation)은프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일기법이다.
JIT는 인터프리터 방식과 정적 방식을 혼합한 방식이다.
인터프리터 방식은 프로그램을 실행할 때마다 컴퓨터가 알아 들을 수 있는 언어로 변환하는 작업을 수행한다.
간편하기는 하지만 성능이 매우 느릴 수 밖에 없다.바이트 코드를 읽어 기계어를 생성 하는데, 코드가 실행되는 과정에서 실시간으로 일어나며 전체코드의 필요한 부분만 변환한다.
하지만 정적 컴파일 방식은 변환 작업을 딱 한번만 한다. 언어로 변환하는 작업을 미리 실행한다.
기본적으로 인터프리터에 의해 수행되지만, 필요한 코드의 정보는 캐시에 담아두었다가(메모리에 담아두었다가 재사용 하게 된다. 정적 컴파일 방식 처럼)
자바 소스코드 -> 자바컴파일러 -> 바이트코드 -> JVM -> 기계어 -> 하드웨어 및 OS의 과정을 거친다.
여기서 JVM -> 기계어로 변환되는 부분을 JIT에서 수행한다.
JIT를 사용하면 반복적으로 수행되는 코드는 매우 빠른 성능을 보이지만, 반대로 처음에 시작할 때에는 변환 단계를 거쳐야 하므로 성능이 느린 단점이 있다.
JVM 구조
클래스 로더(Class Loader)
실행 엔진(Execution Engine)
인터프리터(Interpreter)
JIT 컴파일러(Just-in-Time)
가비지 콜렉터(Garbage collector)
런타임 데이터 영역 (Runtime Data Area)
클래스 로더 JVM 내로 클래스 파일(*.class)을 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다. 런 타임시 동적으로 클래스를 로드하고jar파일 내 저장된 클래스들을 JVM 위에 탑재한다. 즉, 클래스를 처음으로 참조할 때, 해당 클래스를 로드하고 링크한는 역할을 한다.
실행 엔진
클래스를 실행시키는 역할이다.
클래스 로더가 JVM내의 런타임 데이터 영역에 바이트 코드를 배치시키고, 이것은 실행 엔진에 의해 실행된다.
자바 바이트 코드(*.class)는 기계가 바로 수행할 수 있는 언어보다는 비교적 인간이 보기 편한 형태로 기술된 것이다. 그래서 실행 엔진은 이와 같은 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경한다.
인터프리터(interpreter)
자바 컴파일러에 의해 변환된 바이트 코드를 읽고 한 줄씩 기계어로 해석하는 역할을 하는 것이 자바 인터프리터 (interpreter)이다.
원래 JVM에서는 인터프리터 방식만 사용하다가 성능 이슈가 발생해서 JIT 컴파일러를 추가해서 성능을 끌어올렸다. 현 재는 컴파일과 인터프리터 방식을 병행해서 사용한다.
JIT(Just-In-Time)
인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일하여 기계어로 변경하고, 이후에는 해당 더 이 상 인터프리팅 하지 않고 기계어로 직접 실행하는 방식이다.
💡컴파일 임계치(Compile Threshold) - JIT 컴파일러가 메소드가 자주 사용되는 지 체크하는 방식으로 컴파일 임계치를 사용합니다. JIT 컴파일러가 내부적으 로 메서드가 호출될 때마다 호출 횟수를 카운팅하고 그 횟수가 특정 수치를 초과할 때 캐싱해서 이후에는 JIT 컴파일이 트리거된다.
프로그램이 실행 중인 런타임 중에 여러번 호출되는 메소드들을 미리 만들어 둔 해석본을 이용해서 컴파일하는 역할을 하는 것이 JIT 컴파일러이다.
가비지 컬렉터(garbage collector)
가비지 컬렉터(garbage collector)는 메모리 관리를 자동으로 해줘서 개발자가 비즈니스 로직에 더 집중할 수 있게 도와준 다.
Runtime Data Area
Runtime Data Area는 JVM이 프로그램을 수행하기 위해 OS로부터 할당받는 메모리 영역이다.
WAS의 성능에 문제가 발생했을 때, 대부분 이 영역들이 원인이 된다.
Runtime Data Area는 5가지로 구분된다.
PC Register
JVM stack
Native Method stack
Heap
Method Area
좌측 3개의 영역은 Thread별로 생성되고, 우측 2개의 영역은 모든 Thread가 공유한다.
PC Register
Thread가 시작될 때 생성되며 생성될 때마다 생성되는 공간으로, 스레드마다 하나씩 존재한다.
Thread가 어떤 부분을 어떤 명령으로 실행해야할 지에 대한 기록을 하는 부분으로 현재 수행 중인 JVM 명령의 주소를 갖는다.
💡 스레드(thread)란?
스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미
모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행한다.
JVM 스택 영역
Thread의 Method가 호출될 때 수행 정보(메소드 호출 주소, 매개 변수, 지역 변수,
연산 스택)가 Frame 이라는 단위로 JVM stack에 저장된다.
그리고 Method 호출이 종료될 때 stack에서 제거된다.
Native method stack
자바 프로그램이 컴파일되어 생성되는 바이트 코드가 아닌 실제 실행할 수 있는 기계어로 작성된 프로그램을 실행시키는 영역.
JAVA가 아닌 다른 언어로 작성된 코드를 위한 공간.
Java Native Interface를 통해 바이트 코드로 전환하여 저장하게 된다.
일반 프로그램처럼 커널이 스택을 잡아 독자적으로 프로그램을 실행시키는 영역
Method Area (= Class Area = Static area)
Class Loader가 적재한 클래스(또는 인터페이스)에 대한 메타데이터 정보가 저장된다.
- Type(class or interface)의 전체 이름 - Type의 직계 하위 클래스 전체 이름 - Type의 class/interface 여부 - Type의 modifier (public / abstract / final) - 연관된 interface 이름 리스트
Field Information
- Field Type - Field Modifier (public / private / protected / static / final / volatile / transient)
Method Information
- Constructor를 포함한 모든 Method의 메타 데이터를 저장
Runtime Constant Pool
- Type, Field, Method로의 모든 레퍼런스를 저장 - JVM은 이 영역을 통해 실제 메모리 상의 주소를 찾아 참조
Class Variable
- static 키워드로 선언된 변수를 저장 - 기본형이 아닌 static 변수는 레퍼런스 변수만 저장되고 실제 인스턴스는 Heap에 저장됨 - 클래스를 사용하기 이전에 이 변수들은 미리 메모리를 할당받음
Heap 영역
객체를 저장하는 가상메모리 공간.new연산자로 생성되는 객체와 배열을 저장한다.
Class Area(Static Area)에 올라온 클래스들만 객체로 생성할 수 있다.(당연함)
힙은 세 부분으로 나뉘어 진다.
Permanent Generation
직역하면 영구적인 세대이다.
생성된 객체들의 정보의 주소값이 저장된 공간이다. 클래스 로더에 의해 load되는 Class, Method 등에 대한 Meta 정보가 저장되는 영역이고 JVM에 의해 사용된다. Reflection을 사용하여 동적으로 클래스가 로딩되는 경우에 사용된다.
💡 Reflection이란? 객체를 통해 클래스의 정보를 분석해 내는 프로그래밍 기법 구체적인 클래스 타입을 알지 못해도, 컴파일된 바이트 코드를 통해 역으로 클래스의 정보를 알아내어 사용할 수 있다는 뜻이다.
New/Young 영역
이곳의 인스턴스들은 추후 가비지 콜렉터에 의해 사라진다. 생명 주기가 짧은 “젊은 객체”를 GC 대상으로 하는 영역이다.
여기서 일어나는 가비지 콜렉트를Minor GC라고 한다.
Eden:객체들이 최초로 생성되는 공간
Survivor 0, 1:Eden에서 참조되는 객체들이 저장되는 공간
Old 영역
이곳의 인스턴스들은 추후 가비지 콜렉터에 의해 사라진다. 생명 주기가 긴 “오래된 객체”를 GC 대상으로 하는 영역이다. 여기서 일어나는 가비지 콜렉트를Major GC라고 한다. Minor GC에 비해 속도가 느리다.
New/Young Area에서 일정시간 참조되고 있는, 살아남은 객체들이 저장되는 공간이다.
JRE와 JDK 차이점
JRE(Java Runtime Environment)란?
JRE란 번역하면 자바 실행환경으로 자바 프로그램을 실행하는데 필요한 것이다.
즉, 자바 프로그램을 실행시키는데는 문제가 없지만 자바 프로그램을 코딩할 때 jdk가 아니라 jre를 사용하면 문제점이 생길 수 있다.
예를 들어 컴파일이 정상적으로 되지 않을 수도 있다.
JDK(Java Development Kit)란?
번역하면 자바 개발 키트이다. 간단하게 설명하면 자바를 개발하는데 필요한 기능들이 들어간 것이다.
여기에는 물론 자바를 실행하는데 필요한 jre도 포함되어 있어서 jdk를 다운로드 받으면 jre 또한 포함되어 있다.
자바 프로그램 개발을 위해서는 바로 이 jdk를 다운로드 받아 자바 기능을 사용하고 컴파일 해야하는 것이다.
즉, 정리해보면 자바 프로그램을 실행시키는데 필요한 것이 바로 jre이고 자바 프로그램을 개발하는데 필요한 것이 jdk이다.
jdk를 다운로드 받으면 jre도 포함되어 있어 개발한 자바 프로그램을 실행시키는 것까지 가능하다.