6,836.17
전일대비 3.41 (+ 0.05% )
22,546.67
전일대비 50.48 ( -0.22% )
49,500.93
전일대비 48.95 (+ 0.10% )
단계 1) 사용자로부터 요청된 SQL 문장을 잘게 쪼개서 MariaDB 서버가 이해할 수 있는 수준으로 분리 (파스 트리) 한다. - SQL 파싱 (Parsing)
단계 2) SQL 의 파싱 정보 (파싱트리) 를 확인하면서, 어떤 테이블부터 읽고 어떤 인덱스를 읽을지 선택한다. - 옵티마이져
불필요한 조건의 제거 및 복잡한 연산의 단순화
여러 테이블의 조인이 있는 경우 어떤 순서로 테이블을 읽을지 결정
각 테이블에 사용된 조건과 인덱스의 통계 정보를 이용해 사용할 인덱스를 결정
가져온 레코드들을 임시 테이블에 넣고 다시 한번 가공해야 하는지 결정
단계 3) 두번째 단계에서 결정된 테이블의 읽기 순서나 선택된 인덱스를 이용해 스토리지 엔진으로부터 데이터를 가져온다.
수립된 실행 계획대로 스토리지 엔진에 레코드를 읽어오도록 요청하고 MariaDB 엔진에서는 스토리지 엔진으로부터 받은 레코드를 조인하거나 정렬하는 작업진행.
데이터베이스를 이용한 session 처리 핸들러 예제입니다. (* SessionHandlerInterface 인터페이스 구현체,)
아래와 같이 open, close, read, write. destroy, gc 를 구현하면 DB 를 통해서도 세션관리가 가능합니다.
(일반적으로는 프레임워크에서 세션처리 구현이 되어있기 때문에 아래와 같이 작업할 일이 없음)
테이블
소스
<?php
date_default_timezone_set('Asia/Seoul');
/**
* Session Handler Interface
*/
class DatabaseSessionHandler implements SessionHandlerInterface
{
private PDO $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function open($path, $name)
{
return true;
}
public function read($id)
{
$sth = $this->pdo->prepare('SELECT * FROM sessions WHERE `id` = :id');
if ($sth->execute([':id' => $id])) {
if ($sth->rowCount()>0) {
$payload = $sth->fetchObject()->payload();
} else {
$sth = $this->pdo->prepare('INSERT INTO sessions(`id`) VALUES(:id)');
$sth->execute([':id' => $id]);
}
}
return $payload ?? '';
}
public function close()
{
return true;
}
public function destroy($id)
{
$this->pdo
->prepare('DELETE FROM sessions WHERE `id` = :id')
->execute([':id' => $id])
;
}
public function gc($max_lifetime)
{
$sth = $this->pdo->prepare('SELECT * FROM sessions');
if ($sth->execute()) {
while ($row = $sth->fetchObject()) {
$timestamp = strtotime($row->created_at);
if (time() - $timestamp > $max_lifetime) {
$this->destroy($row->id);
}
}
return true;
}
return false;
}
public function write($id, $data)
{
return $this->
pdo->prepare('UPDATE sessions SET `payload` = :payload WHERE `id` = :id')
->execute([':payload' => $data, ':id' =>$id]);
}
}
// 세션 핸들러 등록
session_set_save_handler(new DatabaseSessionHandler(new PDO('mysql:dbname=test;host=127.0.0.1;', 'root', 'root')));
session_start();
$_SESSION['message'] = 'Hello world';
$_SESSION['foo'] = new stdClass();
session_gc(); // 세션 정리
리플렉션을 통해 객체의 메타데이터 (properties, class) 를 확인이 가능하다.
/**
* ReflectionClass
*/
class A
{
private $message = "Hello world";
public function __construct($message)
{
$this->message = $message;
}
}
class B extends A
{
}
// 특정 클래스 "A" 에 대하여 private 의 properties 가져오기
$refClass = new ReflectionClass('\A');
var_dump($refClass->getProperties(ReflectionProperty::IS_PRIVATE));
==> array(1) { [0]=> object(ReflectionProperty)#2 (2) { ["name"]=> string(7) "message" ["class"]=> string(1) "A" } }
// B 클래스가 A 클래스의 자식클래스인지 확인
$refClassB = new ReflectionClass('\B');
var_dump($refClassB->isSubclassOf('\A'));
==> bool(true)
// "message" 이름의 property 정보
$messageProperty = $refClass->getProperty('message');
var_dump($messageProperty);
=> object(ReflectionProperty)#3 (2) { ["name"]=> string(7) "message" ["class"]=> string(1) "A" }
Java 에는 Converter 라는 인터페이스가 있다. 해당 인터페이스는 A 타입 -> B 타입으로 변경을 할 때 사용한다. (ex. String -> Integer)
소스로 예를 들자면 "127.0.0.1:8080" 형태의 String 을 DTO 객체로 변환을 아래와 같이 처리가 가능하다.
1) IpPort DTO
import lombok.Getter;2) String -> IpPort DTO Converter
@Getter
@EqualsAndHashCode
public class IpPort {
private String ip;
private int port;
public IpPort(String ip, int port) {
this.ip = ip;
this.port = port;
}
}
3) 사용import hello.typeconverter.type.IpPort;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
@Slf4j
public class StringToIpPortConverter implements Converter<String, IpPort> {
@Override
public IpPort convert(String source) {
log.info("convert source={}", source);
// 127.0.0.1:8080
String[] split = source.split(":");
String ip = split[0];
int port = Integer.parseInt(split[1]);
return new IpPort(ip, port);
}
}
@Test
void StringToIpPort() {
StringToIpPortConverter converter = new StringToIpPortConverter();
String source = "127.0.0.1:8080";
IpPort result = converter.convert(source);
assertThat(result).isEqualTo(new IpPort("127.0.0.1", 8080));
}
간략요약하자면
UPDATE, DELETE 를 한다고 해서 Postgresql 은 물리적인 공간이 사라지는 것은 아니다. (Live Tuple -> Dead Tuple 이 되는 것임)
따라서 물리적인 공간을 지우기 위해서는 "vacuum" 이라는 것을 제공한다.
- auto vacuum : https://nrise.github.io/posts/postgresql-autovacuum/ 참조
- alter table vauum (* 컬럼타입을 변경하는 것만으로도 full vacuum 과 동일한 효과)