# 6월 16일 스터디 ## 최지수 ### 인덱스 스캔의 종류 - Range Scan 인덱스 루트 블록에서 리프 블록까지 수직적으로 탐색한 후에 리프 블록을 필요한 범위만 스캔하는 방식 B+Tree의 가장 일반적이고 정상적인 엑세스 방식 인덱스를 구성하는 선두 컬럼이 조건절에 사용되어야 가능하다. 이 스캔 과정을 거쳐 생성된 결과 집합은 인덱스 컬럼 순으로 정렬을 생략하거나 min, max값을 빠르게 추출 가능 - Full Scan 수직적 탐색 없이 리프 블록을 처음부터 끝가지 수평적으로 탐색하는 방식 최적의 인덱스가 없을 때 차선으로 선택 - Unique Scan 수직적 탐색만으로 데이터를 찾는 방식 작동시점 - unique인덱스를 통해 '=' 조건으로 탐색하는 경우 중복되지 않는 unique한 값을 '=' 조건으로 검색할 경우 데이터 하나를 찾는 순간 더이상 탐색하지 않음 - Skip Scan 조건절에 빠진 인덱스 선두 컬럼의 distinct value 개수가 적고 후행 컬럼의 distinct value 개수가 많을 때 사용 루트 또는 브랜치 블록에서 읽은 컬럼 값 정보를 이용해 조건에 부합하는 레코드를 포함할 가능성이 있는 리프 블록만 골라서 액세스 하는 방식 첫 번째 리프 블록과 마지막 리프 블록은 항상 방문 인덱스의 전체 조건을 이용하지 않고 부분적으로 이용할 경우 사용 ### B+Tree 인덱스 동작 과정 B+Tree는 Root - Branch - Leaf로 이루어져 있다. 수직적 탐색을 통해 인덱스 스캔 시작 지점을 찾고 수평적 탐색으로 데이터를 찾는다. **수직적 탐색** 수평적 탐색을 위한 시작 지점을 찾는 과정이다. 찾고자 하는 값이 들어가 있을 범위를 찾아 하위 블록으로 이동한다. 이 과정을 반복하면서 하위 블록에서 조건을 만족하는 첫 번째 레코드를 찾는 과정이다. 루트 블록에서 리프 블록까지 아래 쪽으로 탐색을 진행하기 때문에 수직적 탐색이라고 한다. **수평적 탐색** 수직적 탐색을 통해 스캔 지점을 찾고 난 후 리프 블록을 수평적으로 스캔하면서 데이터를 찾는 과정이다. 리프 블록끼리는 서로 앞 뒤 블록에 대한 주소 값을 가지고 있기 때문에 수평적인 탐색이 가능하다. 탐색을 하다가 해당 범위를 넘어서는 값을 만나면 멈춘다. --- ## 문예지 ### call by value vs call by reference > **js data type** > - 기본 데이터 타입: null, number, string, boolean, undefinded, symbol(ES6에 추가) - objects: Array, Object >**call by value** > js의 기본 데이터 타입은 변경 불가능함(immutable) + 이들 값은 런타임(변수 할당 시점)에 메모리의 스택 영역(Stack Segment)에 고정된 메모리 영역을 점유하고 저장 ```javascript= let a = 1; let b = a; // a, b는 서로 다른 메모리 주소 참조 console.log(a, b); // 1 1 console.log(a === b); // true a = 10; console.log(a, b); // 10 1 console.log(a === b); // false ``` >**call by reference** > 값이 아닌 참조(모두 동일한 위치)로 전달 기본 데이터가 아닌 유형(non-primitive data type)의 변수를 함수의 매개 변수로 전달되면, 원래 변수의 참조가 업데이트 ```javascript= let nameObj = { name: '1111' } function changeName(nameObj) { return nameObj.name = '2222' } console.log(changeName(nameObj)) // 2222 console.log(nameObj) // 2222 ``` <br> > 작은 오해와 진실 > ```javascript= let obj = { p1: 10 } let copy = obj console.log(obj === copy) copy = { p2: 100 } console.log(obj === copy) ``` 위 코드에 대한 내 예상 true true 이유 ![](https://i.imgur.com/uiQwIWs.png) 아니었다 :arrow_right: 재할당! ```javascript= let obj = { p1: 10 } let copy = obj console.log(obj === copy) // true copy = { p2: 100 } console.log(obj === copy) // false ``` ![](https://i.imgur.com/wL1RgUs.png) 내가 생각한대로 바뀌는건 요거 ![](https://i.imgur.com/wCWkcOC.png) <br> ### Nodejs의 내부 동작 ![](https://i.imgur.com/GNZcKIn.jpg) <br> > 이벤트루프가 왜 중요한가? > - 이벤트루프는 메인스레드 겸 싱글스레드로서, 비즈니스 로직을 수행 - 수행 도중에 블로킹 IO작업을 만나면 커널 비동기 또는 자신의 워커 쓰레드풀에게 넘겨주는 역할 - 동시에 많은 요청이 들어온다해도 1개의 이벤트루프에서 처리 - JS로직(if분기, 반복문 등)이 무겁다면 많은 요청을 처리해내기 힘들 것 - 이벤트루프가 바쁘면(= cpu인텐시브 작업) 메모리가 부족해서 뻗을 것 <br> > libuv는 어떻게 동작하는가? > - libuv는 윈도우 커널, 리눅스 커널을 추상화해서 wrapping - Nodejs는 기본적으로 libuv 위에서 동작하며, Node 인스턴스가 뜰 때, libuv에는 워커 쓰레드풀(default 4개)이 생성 - 블로킹 작업(api콜, DB Read/Write 등)들이 들어오면 이벤트루프가 uv_io에게 넘김 - libuv는 커널단에서 어떤 비동기 작업들을 지원해주는지 알고 있기때문에, 그런 종류의 작업들을 받으면, 커널의 비동기 함수들을 호출 - 작업이 완료되면 시스템콜을 libuv에게 넘김 - libuv 내에 있는 이벤트루프에게 콜백으로서 등록 - libuv의 워커쓰레드는 커널이 지원안하는 작업들을 수행 <br> > 이벤트루프의 내부 동작과정 > **이벤트루프의 phase** 이벤트루프는 몇 개의 phase 들로 구성 각 phase 들은 FIFO 큐를 가지고 있으며, 이 큐에는 특정 이벤트의 콜백들을 넣고, CPU가 할당(=이벤트루프가 해당 phase를 호출할때)될 때 실행 - timers - setTimeout()과 setInterval() 과 같은 타이머 콜백 처리 - 타이머 콜백들을 받고 이걸 poll 큐에 등록 - 타이머 콜백 내부로직들은 poll큐에 먼저 등록된 콜백들이 처리되고 나중에 처리될 수도 있으므로, 파라미터로 지정한 시간에 딱 실행됨을 보장하지 못함 - I/O callbacks - 클로즈 콜백, 타이머로 스케줄링된 콜백, setImmediate()를 제외한 거의 모든 콜백들을 집행 - idle, prepare - 내부용으로만 사용 (모든 큐가 비어있으면 idle이 되면서 tick frequency가 떨어짐 = 할 일도없으니 이벤트루프가 천천히 돎) - poll - poll 큐가 비어있음 : setImmediate()가 있으면 check로 넘어감. 없으면 이벤트루프가 phase를 돌며 콜백을 무한히 기다림 => - poll 큐에 뭐가있음 : 이벤트루프가 큐를 순회하며 처리함 - check setImmediate() 콜백은 여기서 호출되고 집행 - close callbacks .on('close', ...) 같은 것들이 여기서 처리됨