728x90

들어가며,

 

JdbcTemplate은 스프링 프레임워크에서 제공하는 JDBC의 간소화된 버전으로, 템플릿 콜백 패턴을 사용하여 반복적인 JDBC 코드를 더 간편하게 작성할 수 있도록 도와줍니다.

JdbcTemplate이 제공하는 메서드들은 내부적으로 Connection, Statement, ResultSet 등의 자원을 생성하고 관리하여, 이러한 자원을 직접 다룰 필요가 없습니다.

 

템플릿 콜백 패턴?

템플릿 콜백 패턴은 클래스의 메서드가 호출되면서 특정 동작을 수행하는 디자인 패턴입니다.

JDBC Template에서는 JdbcTemplate 클래스를 사용하며, 이 패턴은 주로 execute() 메서드와 함께 사용됩니다.

 

Row Mapper를 콜백 인터페이스로 사용해, DB에서 조회한 결과를 객체에 매핑하는 역할을 합니다.

 

import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;

public class EmployeeRowMapper implements RowMapper<Employee> {

    @Override
    public Employee mapRow(ResultSet resultSet, int rowNum) throws SQLException {
        Employee employee = new Employee();
        employee.setId(resultSet.getLong("id"));
        employee.setName(resultSet.getString("name"));
        employee.setSalary(resultSet.getDouble("salary"));
        // 필요한 다른 속성들 설정
        return employee;
    }
}

 

기존의 JDBC에서 ResultSet을 사용해 처리하던 부분을 Row Mapper를 통해 해결한다.
// 반복문을 통해 ResultSet 결과 처리
while (resultSet.next()) {
    // 결과 처리
    String columnValue = resultSet.getString("column_name");
    // 추가로 처리할 작업 수행
}

// rowMapper를 이용해 구현
while(resultSet 이 끝날 때까지) {
rowMapper(rs, rowNum)
}

 

Statement는 어떻게 대신 처리해주나?

update(): INSERT, UPDATE, DELETE 등의 쿼리를 실행할 때 사용됩니다. 내부적으로 Statement 관리를 처리합니다.

String sql = "update item set item_name=?, price=?, quantity=? where id=?";
template.update(sql,
    itemName,
    price,
    quantity,
    itemId);

 

추가로 batchUpdate()메서드를 이용하면 여러 개의 SQL 문을 일괄로 처리할 수 있습니다.

update 메서드를 이용해서 파라미터를 바인딩하여 SQL문을 처리할 수 있습니다.
그러나, 이렇게 순서대로 바인딩 하는 경우, 순서의 변경으로 인해 데이터 장애가 발생할 위험이 있습니다.

NamedParameterJdbcTemplate

이름을 지정해서 바인딩하여 위와 같은 문제를 해결할 수 있습니다.

@Slf4j
@Repository
public class JdbcTemplateItemRepositoryV2 implements ItemRepository {
    
    private final NamedParameterJdbcTemplate template;
    
    public JdbcTemplateItemRepositoryV2(DataSource dataSource) {
    	this.template = new NamedParameterJdbcTemplate(dataSource);
    }
    
    @Override
    public Item save(Item item) {
        String sql = "insert into item (item_name, price, quantity) values (:itemName, :price, :quantity)";
        SqlParameterSource param = new BeanPropertySqlParameterSource(item);
        KeyHolder keyHolder = new GeneratedKeyHolder();
        template.update(sql, param, keyHolder);
        
        Long key = keyHolder.getKey().longValue();
        item.setId(key);
        return item;
    }

 

이름 지정 파라미터를 사용하는 방식에서는 Map과 같이 Key, Value 데이터 구조로 전달해야 합니다.

자주 사용하는 종류는 아래와 같습니다.
Map
SqlParameterSource
 - MapSqlParameterSource
 - BeanPropertySqlParameterSource

 

Map은 단순히 Map<String, Object>를 Map.of("id", id)로 받아 param 으로 사용합니다.

MapSqlParameterSource는 .addValue를 통해 부분적으로 조정이 가능하다. Update()메서드와 같은 경우에 사용이 가능합니다.

대부분의 경우는 BeanPropertySqlParameterSource를 사용합니다.

이는 데이터 값을 자동으로 생성해주기 때문에 

 

SimpleJdbcInsert

private final SimpleJdbcInsert jdbcInsert;
public JdbcTemplateItemRepositoryV3(DataSource dataSource) {
    this.template = new NamedParameterJdbcTemplate(dataSource);
    this.jdbcInsert = new SimpleJdbcInsert(dataSource)
    .withTableName("item")
    .usingGeneratedKeyColumns("id");
public Item save(Item item) {
    SqlParameterSource param = new BeanPropertySqlParameterSource(item);
    Number key = jdbcInsert.executeAndReturnKey(param);
    item.setId(key.longValue());
    return item;
}

 

jdbcInsert.excuteAndReturnKey(param)을 사용해 Insert SQL을 실행할 수 있습니다.

 

 

728x90

들어가며,

2024.01.13 - [Spring Framework/JDBC, Mybatis,JPA, Querydsl] - JDBC의 이해, JDBC란? 개념

 

JDBC의 이해, JDBC란? 개념

JDBC란? JDBC는 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API다. SQL Command를 보내면 Result Set 응답을 받는 식으로 동작하고 있다. 그러나 JDBC를 통해 데이터베이스에 접속할 수는 있지만,

wooltech.tistory.com

 

JDBC를 통해 DB에 접근하면서 Conntection -> Statement -> ResultSet으로 동작하는 것에 대해 공부했습니다.

DB에 접근할 때마다 Connection을 생성해 연결하고, 연결을 종료하는 과정을 거쳐야 했습니다.

그러나, 이러한 방식은 DB에 자주 접근하는 경우에 리소스 사용과 관련해 많은 단점이 있습니다.

 

이번 편에서는 위 문제를 해결하기 위한 Connectio Pool이라는 개념에 대해 정리하려고 합니다.

 

Connection Pool 이란?

 

Connectio Pool이란 미리 Pool에 Connection을 생성해두고 필요한 경우에 꺼내 사용하는 것을 말합니다.

이 Connection들은 이미 DB와 연결이 되어 있기 때문에, Driver를 통해 꺼내서 SQL를 전달할 수 있습니다.

사용이 완료된 후에도 이전처럼 연결을 종료하는 것이 아닌 그대로 Pool에 반환하게 됩니다.

이로써 연결된 Connection들을 지속적으로 사용할 수 있습니다.

 

Spring Boot에서는 기본적으로 HikariCP를 통해 Connection Pool을 관리하고 있습니다.

 

Data Source란?

 

Data Source는 Connection을 조회하는 역할을 합니다.

하단의 검은색 박스처럼 Connection을 획득하는데는 다양한 방법이 있습니다. 

항상 JAVA에서는 이런 여러 방식의 사용에 유연함을 주기 위해 추상화를 통해 해결하고 있습니다.

 

Driver Manager와 Data Source 사용 비교

@Slf4j
public class ConnectionTest {
    @Test
    void driverManager() throws SQLException {
        Connection con1 = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        Connection con2 = DriverManager.getConnection(URL, USERNAME, PASSWORD);
        log.info("connection={}, class={}", con1, con1.getClass());
        log.info("connection={}, class={}", con2, con2.getClass());
    }
    @Test
    void dataSourceDriverManager() throws SQLException {

	DriverManagerDataSource dataSource = new DriverManagerDataSource(URL,
        USERNAME, PASSWORD);
        useDataSource(dataSource);
    }
    private void useDataSource(DataSource dataSource) throws SQLException {
        Connection con1 = dataSource.getConnection();
        Connection con2 = dataSource.getConnection();
        log.info("connection={}, class={}", con1, con1.getClass());
        log.info("connection={}, class={}", con2, con2.getClass());
    }
}

 

Driver Manager는 항상 URL, USERNAME, PASSWORD를 입력하여 커넥션을 획득하지만, 
Data Source는 처음에 Driver Manager Data Source 객체를 생성할때만 파라미터를 넘겨주고나면 커넥션은 get메서드를 통해 호출할 수 있다.

 

 

 

728x90

JDBC란?

JDBC는 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API다.

 

 

SQL Command를 보내면 Result Set 응답을 받는 식으로 동작하고 있다.
그러나 JDBC를 통해 데이터베이스에 접속할 수는 있지만, MySQL, Oracle 등 많은 종류의 DB가 존재하고 각 사용법이 달라 DB를 바꾸게 되면 쿼리를 새로 작성해야 하는 번거로움이 있다.

따라서 SQL쿼리를 작성하면, 각 DB 성질에 맞게 변환해주는 SQL Mapper가 등장했다. Mybatis, Jdbc Template이 그 예시다. 그 후로 SQL쿼리마저 작성해주는 JPA의 등장, 동적 쿼리도 만들어주는 Querydsl까지 발전해온 내용을 기록하려고 한다.

 

데이터베이스를 변경하게 된다면?

각 DB는 어플리케이션과 Connection을 통해 연결된 후에 SQL과 결과를 주고 받으며 통신한다.

만약 데이터베이스를 변경하는 경우에는 코드를 모두 리팩토링해야 하는 일이 생기게 된다. 이를 해결하기 위해 등장한 것이 JDBC 표준 인터페이스다.

 

DB 연결 환경 설정

package hello.jdbc.connection;

public abstract class ConnectionConst {
    public static final String DB_URL = "jdbc:h2:tcp://localhost/~/test";
    public static final String DB_USER = "sa";
    public static final String DB_PASSWORD = "";
}
@Log4j2
public class DBConnectionUtil {

    public static Connection getConnection() {
        try {
            Connection connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
            log.info("connection = {}, class = {}", connection, connection.getClass());
            return connection;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

 

별도의 클래스를 생성해 어플리케이션이 DB에 접근할 수 있도록 Connection을 연결한다.

DriverManager는 각 DB에 맞게 커넥션 연결을 도와주는 역할을 한다.
기입된 url, user, password 등의 정보를 전달하고 커넥션을 반환한다.

테스트 코드 작성

@Slf4j
class DBConnectionUtilTest {
    @Test
    void connection() {
        Connection connection = DBConnectionUtil.getConnection();
        assertThat(connection).isNotNull();
	}
}

 

위에서 작성한 DBConnectionUtil을 통해 Connection이 제대로 동작하는지 테스트해 볼 수 있다.

 

Connection 이후에 Statement와 ResultSet

 

Connection으로 연결이 되면, Statement 객체를 생성해서 SQL 쿼리를  전달하게 된다.
전달 받은 SQL 쿼리문은 ResultSet 객체에서 결과 반환을 위해 동작하게 되는데, ResultSet은 Cursor를 이용하는 동작하는 방식을 따른다.
// DriverManager를 통해 Connection 연결
Connection connection = DriverManager.getConnection(url, username, password);

// Connection 객체를 통해 Statement 객체 생성
Statement statement = connection.createStatement();

//executeQuery메서드를 통해 SQL쿼리를 전달하고 ResultSet 생성
String sqlQuery = "SELECT * FROM your_table";
ResultSet resultSet = statement.executeQuery(sqlQuery);

// 반복문을 통해 ResultSet 결과 처리
while (resultSet.next()) {
    // 결과 처리
    String columnValue = resultSet.getString("column_name");
    // 추가로 처리할 작업 수행
}

// 연결 종료 : 마지막에 동작한 순서대로 연결을 종료
resultSet.close();
statement.close();
connection.close();​


Cursor를 통해 row를 돌며 동작하는 ResultSet

 

TODO: 예외 처리 및 트랜잭션 관리 등을 고려해야 한다.

 

 

728x90

들어가며,

어플리케이션을 만드는데 기본적으로 사용되는 MVC 패턴을 공부하기 전에 Servlet과 JSP에서 MVC패턴으로 변화되는 과정에서 중요한 패턴인 프론트 컨트롤러에 대해 공부해보려고 한다.

MVC패턴은 request를 받아서 컨트롤러가 정상적인 HTTP 요청인지 확인 후 서비스, 리포지토리 계층에 요청을 전달한다. 그러면 서비스 계층에서는 비즈니스 로직을, 리포지토리에서는 DB로 전달을 하게 된다.
이 동작을 통해 Model이라는 도메인에 데이터를 전달하고 참조할 수 있게 되는 것이다.
마지막으로 View에서는 response를 내보내면 client가 확인할 수 있다.

프론트 컨트롤러란?

 

이전에 request를 Servlet으로 받아 처리할 때는 각 컨트롤러가 직접 요청을 받아 처리했는데 Front Controller를 도입하면 먼저 Front Controller에서 클라이언트의 모든 요청 값들을 받아서 각 컨트롤러에 맞게 전달해준다.
이를 통해 요청 처리의 일관성을 유지하고 중복 코드를 줄일 수 있으며, 보안, 로깅 등의 부가적인 작업을 효율적으로 수행할 수 있다.

 

@WebServlet(name = "frontControllerServletV1", urlPatterns = "/front-controller/
v1/*")
public class FrontControllerServletV1 extends HttpServlet {
	private Map<String, ControllerV1> controllerMap = new HashMap<>();
	public FrontControllerServletV1() {
		controllerMap.put("/front-controller/v1/members/new-form", new MemberFormControllerV1());
		controllerMap.put("/front-controller/v1/members/save", new MemberSaveControllerV1());
		controllerMap.put("/front-controller/v1/members", new MemberListControllerV1());
	}

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    System.out.println("FrontControllerServletV1.service");
    String requestURI = request.getRequestURI();
    
    ControllerV1 controller = controllerMap.get(requestURI);
    if (controller == null) {
    	response.setStatus(HttpServletResponse.SC_NOT_FOUND);
    	return;
    }
    controller.process(request, response);
    }
}

 

FrontControllerServletV1 생성자를 통해 등록된 controllerMap을 통해 클라이언트 요청을 각 컨트롤러에 매핑한다.
service 메서드는 HttpServletRequest와 Response를 받아서 URI를 조회하고 controllerMap에서 객체를 찾아온다.
객체 값이 null인 경우 NOT_FOUND 예외 처리를 한다.
request와 response를 받아 process 메서드를 동작한다.

 

controllerMap 구성

  • key: 매핑 URL
  • value: 호출될 컨트롤러 

public class MyView {
    private String viewPath;
    public MyView(String viewPath) {
        this.viewPath = viewPath;
    }
    public void render(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        RequestDispatcher dispatcher = request.getRequestDispatcher(viewPath);
        dispatcher.forward(request, response);
    }
}

 

MyView라는 중간 단계를 두어 Controller가 Jsp에 직접 전달하지 않고 Front Controller를 거치게 설계됐다.

 

@WebServlet(name = "frontControllerServletV2", urlPatterns = "/front-controller/
v2/*")
public class FrontControllerServletV2 extends HttpServlet {
    private Map<String, ControllerV2> controllerMap = new HashMap<>();
    public FrontControllerServletV2() {
        controllerMap.put("/front-controller/v2/members/new-form", new MemberFormControllerV2());
        controllerMap.put("/front-controller/v2/members/save", new MemberSaveControllerV2());
        controllerMap.put("/front-controller/v2/members", new MemberListControllerV2());
    }
    
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String requestURI = request.getRequestURI();
        
        ControllerV2 controller = controllerMap.get(requestURI);
        if (controller == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        MyView view = controller.process(request, response);
        view.render(request, response);
        }
    }

 

이제 각각의 Controller는 MyView만 반환하면, 프론트 컨트롤러에서 render메서드를 통해 일관되게 처리해준다.

 

public class ModelView {

	private String viewName;
	private Map<String, Object> model = new HashMap<>();
    
    public ModelView(String viewName) {
    	this.viewName = viewName;
    }
    public String getViewName() {
    	return viewName;
    }
    public void setViewName(String viewName) {
   		this.viewName = viewName;
    }
    public Map<String, Object> getModel() {
    	return model;
    }
    public void setModel(Map<String, Object> model) {
    	this.model = model;
    }
}

 

서블릿 종속성을 제거하는 것이 목적이다.
1. ModelView를 통해 컨트롤러는 논리 View 이름을 반환하게 되며, 이를 View Resolver를 통해 실제 View 경로로 변경해준다.
public class MemberSaveControllerV3 implements ControllerV3 {
    
    private MemberRepository memberRepository = MemberRepository.getInstance();
    
    @Override
    public ModelView process(Map<String, String> paramMap) {
        String username = paramMap.get("username");
        int age = Integer.parseInt(paramMap.get("age"));
        
        Member member = new Member(username, age);
        memberRepository.save(member);
        
        ModelView mv = new ModelView("save-result");
        mv.getModel().put("member", member);
        return mv;
    	}
    }​

 


2. 요청 파라미터에서 서블릿을 제거하고 Map을 통해 넘겨준다.

 

Spring MVC 구조

 

Spring MVC 구조에서는 Front Controller가 Dispatcher Servlet으로 구성되어 있다. 
이외에도 어댑터를 통해 핸들러(컨트롤러)를 유연하게 사용할 수 있다는 장점이 있다.
728x90

 

Pub/Sub은 자주 사용되는 시스템 디자인 패턴 중 하나이다.
Redis는 메시지 브로커 역할을 하며, Publisher가 발행하는 메시지를 Subscriber에게 전달한다.

실시간 애플리케이션, 채팅 시스템, 알림 등을 빌드하기 위한 강력한 기능이다. 인메모리의 장점을 가지고 있어 빠른 처리가 가능하지만, 단점 또한 존재해 kafka등의 시스템을 사용하는 것도 고려해야 할 필요가 있다.

 

코드로 구현

@Service
@Slf4j
public class MessageListenerService implements MessageListener {
    @Override
    public void onMessage(Message message, byte[] pattern) {
      log.info("Received Channel: {} Channel: {}" , new String(message.getChannel()), new String(message.getBody()));

    }
}

 

메세지를 받는 서비스를 MessageListener 인터페이스를 통해 Redis의 "chat" 채널에서 발생하는 메시지를 수신하고,
onMessage메서드를 통해 수신된 메시지와 채널 정보를 로깅하는 간단한 Pub/Sub 시스템을 구현했습니다.

 

@Component
public class RedisConfig {

    @Bean
    MessageListenerAdapter messageListenerAdapter() {
        return new MessageListenerAdapter(new MessageListenerService());
    }

    @Bean
    RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory, MessageListenerAdapter listener) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listener, ChannelTopic.of("chat"));
        return container;
    }

 

  • RedisConnectionFactory는 Redis와의 연결을 나타내는 Spring의 빈입니다.
  • MessageListenerAdapter는 실제 메시지를 처리할 리스너입니다.
  • ChannelTopic.of("chat")는 구독할 Redis 채널을 지정합니다. 위 코드에서는 "chat" 채널에 대한 리스너가 등록되어 있습니다.

API로 메시지 전달

@RestController
@RequiredArgsConstructor
public class PublishController {
    
    private final RedisTemplate<String, String> redisTemplate;
    
    @PostMapping("/publish")
    public void publish(String message) {
        redisTemplate.convertAndSend("chat", message);
    }
}

 

RedisTemplate을 주입받아 메시지를 발행하는 API를 구현했습니다.

'DB(MySQL, MongoDB, Redis, Kafka) > Redis' 카테고리의 다른 글

Spring Boot에서 Redis Cache 사용  (0) 2024.01.06
Redis 데이터 타입과 기본 명령어  (0) 2024.01.05
Redis 기본 개념  (1) 2024.01.05
728x90

들어가며,

앞서 Redis에 대한 개념과 기본 명령어를 통해 CLI 환경에서 Redis를 사용했다.

이제 본격적으로 Redis의 캐싱, 세션, pub sub 등에 대해 공부할 예정이다.

캐싱

2024.01.05 - [DB(MySQL, MongoDB, Redis, Kafka)/Redis] - Redis 기본 개념

기본 개념에서 공부했듯이 Redis는 인메모리 데이터베이스로 디스크에 저장하고 읽어 오는데 드는 비용을 줄이고 속도를 빠르게 하기 위해 사용된다. 

이때 사용되는 기능이 Cache이다. 

  1. 캐시 내부를 확인
  2. 없으면, 데이터베이스를 확인
  3. 캐시에 데이터를 저장

이 구조로 사용된다. 그러나 메모리는 휘발성이 높은 단점이 있었는데, 캐시에 저장된 데이터는 정책상 정해놓은 시간이 소요되면 자동으로 삭제된다.

초기 yml 설정

spring:
  data:
    redis:
      host: 127.0.0.1
      port: 6379
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/redis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root
    password: 1234
  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true

 

redis에 대한 설정, Mysql에 대한 설정 이렇게 두가지를 먼저 설정하는 것이 중요하다.
redis와 Mysql은 모두 docker로 컨테이너에서 띄워 사용.

 

캐시를 적용하기 위한 간단한 회원 로직 작성

 

Repository에서 Jpa연동을 설정하고 Service에서 다시 Jpa 기본 메서드인 findById를 통해 id값으로 조회하는 로직을 작성한다.
Controller는 HTTP GET 요청에 따라 동작한다.
코드는 간단한 구현이라 생략합니다.

 

Redis Config 설정

@Configuration
public class RedisConfig {

    @Bean
    RedisTemplate<String, Member> memberRedisTemplate(RedisConnectionFactory connectionFactory) {
        var objectMapper = new ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .registerModule(new JavaTimeModule())
                .disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);

        var template = new RedisTemplate<String, Member>();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new Jackson2JsonRedisSerializer<>(objectMapper, Member.class));
        return template;
    }
}

 

RedisTemplate을 사용하기 위해 config 클래스를 만들었다. 이 RedisTemplate은 문자열 키와 Member객체로 이루어진다.

RedisConnectionFactory를 통해 Redis서버와 연결을 돕는다. 여기에는 Lettuce 또는 Jedis 등이 있다. 
Key와 Value를 설정하는데, Value에 들어가는 Member 객체를 Json으로 받기 위해 Jackson2JsonRedisSerializer를 사용한다. (몇가지 오류를 방지하기 위해 objectMapper를 따로 위에서 만들어 둠)

 

public Member getMember(Long id) {
    var key = "member:" + id;

    var cacheMember = redisTemplate.opsForValue().get(key);
    if (cacheMember != null) {
        return cacheMember;
    }
    Member member = memberRepository.findById(id).orElseThrow();
    redisTemplate.opsForValue().set(key, member);
    return member;

}

 

다시 Service 계층으로 돌아와 Template을 활용해 아래의 로직을 작성하면 된다.

캐시 내부를 확인 -> 없으면, 데이터베이스를 확인 -> 캐시에 데이터를 저장

 

 

'DB(MySQL, MongoDB, Redis, Kafka) > Redis' 카테고리의 다른 글

Redis Pub/ Sub  (0) 2024.01.10
Redis 데이터 타입과 기본 명령어  (0) 2024.01.05
Redis 기본 개념  (1) 2024.01.05
728x90

키-밸류 스토어

Redis는 고성능의 키-밸류 스토어로, 주로 데이터베이스의 캐시로 사용됩니다. 이를 통해 데이터베이스 조회 시간을 단축시키고, 시스템 전반의 성능을 향상시킬 수 있습니다.

  • Cache Aside 패턴: 클라이언트가 데이터를 요청하면, 먼저 Redis 캐시에서 데이터의 존재 유무를 확인합니다. 캐시에 데이터가 있다면 (Cache Hit) 캐시의 데이터를 사용하고, 없다면 (Cache Miss) 실제 데이터베이스에서 데이터를 가져와 캐시에 저장한 후 클라이언트에 반환합니다.
  • Write Back 패턴: 데이터베이스에 쓰기 작업이 많은 경우, 캐시에 데이터를 먼저 저장하고 주기적으로 데이터베이스에 배치로 업데이트하여 성능을 향상시킵니다.

String 타입

기본적인 데이터 타입으로, 최대 크기는 512MB입니다. text, byte를 저장, 원자적 연산 기능을 하며, 아래 명령어를 통해 조작 가능합니다.

  • SET
  • SETNX
  • GET
  • MGET
  • INC
  • DEC

List and Set

  • Lists: Linked-list 형태로, 데이터 추가 및 삭제가 빠릅니다. lrange, lpush, rpush 등의 명령어를 사용합니다.
  • Sets: 중복을 허용하지 않는 유니크한 값의 집합입니다. sadd, smembers 등의 명령어를 사용합니다.

  • PUSH
  • POP

push와 pop을 통해 값을 삽입하고 추출한다. stack의 경우 가장 최근에 넣은 값부터 반출되지만, queue는 가장 처음에 넣은 값부터 순서대로 반출되는 차이를 가진다.

Hash

Hashes: 하나의 키 아래 여러 개의 필드-값 쌍을 저장합니다.

  • HSET
  • HGET
  • HGETALL

Sorted Set

Sorted Sets: 유니크한 값의 집합이며, 각 값은 연관된 점수로 정렬됩니다.

  • ZADD
  • ZREM
  • ZRANGE
  • ZCARD
  • ZRANK

세션 스토어

  • 세션 불일치 문제: 여러 서버를 운영할 때, 클라이언트의 세션이 서버마다 다를 수 있는 문제가 발생할 수 있습니다.
  • Redis 세션 스토어 사용: Redis를 사용하여 여러 서버 환경에서도 세션 데이터를 일관성 있게 관리할 수 있습니다. 이를 위해 connect-redis와 같은 라이브러리를 사용하여 Express와 Redis를 연결할 수 있습니다

 

'DB(MySQL, MongoDB, Redis, Kafka) > Redis' 카테고리의 다른 글

Redis Pub/ Sub  (0) 2024.01.10
Spring Boot에서 Redis Cache 사용  (0) 2024.01.06
Redis 기본 개념  (1) 2024.01.05
728x90

in-memory DB

인메모리 데이터베이스란 디스크가 아닌 메모리에 데이터를 저장해서 사용한다는 의미다.

특징은 크게 3가지가 있다.

  • 빠른 속도 
  • 휘발성
  • 다양한 자료구조

ACID 원칙은?

  • 원자성
  • 일관성
  • 고립성
  • 내구성

데이터베이스가 가져야할 4가지 원칙에서 인메모리 데이터베이스는 위 3가지는 지켜지지만, 휘발성이라는 특성으로 인해 내구성을 지키기 어렵다. 따라서 추가적인 조치가 필요하며, 이에 따른 방법은 스냅샷, 트랜잭션 로깅, NVRAM 등이 있다.

 

Redis

Redis는 캐싱, 메시지 브로커 역할을 하는 오픈소스 형태의 인메모리 데이터베이스다.
하지만, 이것은 기존의 디스크 저장 방식의 데이터 베이스가 필요하지 않는 상황에서 사용된다.
디스크가 아닌 메모리에 데이터를 읽고 쓰기 때문에 매우 빠른 성능을 제공한다.

 

Redis 역시 앞서 말한 인메모리 DB의 약점인, 데이터 손실, 내구성이 약하다는 단점을 가지고 있다. 이를 해결하기 위해 Persistence기능을 제공한다. 이 기능을 통해 충격을 받거나 다운될 때 디스크에 데이터를 저장하고 복원할 수 있도록 한다.

Redis persistence 기능은 몇가지 방법이 있다. 

  • 스냅샷
  • AOF

다만, 스냅샷은 스냅샷 간의 충돌에서 데이터 손실이 일어날 수 있고, AOF는 스냅샷에 비해 느려지는 속도와 리소스를 많이 사용하는 단점이 있다

 

Redis의 형태

 

Redis는 key - Value 형태로 구성되며, 다양한 자료구조를 개발자가 활용할 수 있도록 도와준다.

위 그림에서도 보이듯이, 해시 테이블을 구현하여 고유한 키와 데이터 값에 대한 포인터를 저장하는 Nosql 데이터베이스 유형이다. 

 

다른 데이터베이스와의 차이

특징/
유형
관계형 데이터베이스 그래프 데이터베이스 문서 데이터베이스 키-값 데이터베이스
데이터 모델 테이블(관계형) 그래프 문서(반구조화) 키-값 쌍
데이터 관계 복잡한 관계 지원 그래프 구조 내 관계 지원 문서 내에 중첩된 데이터 지원 독립된 키-값 쌍, 관계 없음
쿼리
언어
SQL 그래프 쿼리 언어 풍부한 쿼리 언어 (JSON 쿼리) key 로만 데이터 액세스
성능 복잡한 쿼리 및 JOIN 수행 가능 관계 트리버스 및 관계 쿼리에 특화 유연한 데이터 모델과 쿼리 가능 뛰어난 성능, 단순한 구조
사용
사례
구조화된 데이터 및 관계가 필요한 경우 네트워크, 추천, 사회적 관계에 중점 반구조화되거나 반-정형 데이터 관리 단순한 키-값 쌍 저장에 중점
확장성 일반적으로 수직적 확장 일반적으로 수직적 및 수평적 확장 일반적으로 수직적 및 수평적 확장 수평적 확장에 적합
고가용성 복제 및 클러스터링으로 고가용성 보장 높은 연결성과 데이터 모델에 기반한 고가용성 복제 및 샤딩을 통한 고가용성 복제 및 클러스터링으로 고가용성 보장

 

'DB(MySQL, MongoDB, Redis, Kafka) > Redis' 카테고리의 다른 글

Redis Pub/ Sub  (0) 2024.01.10
Spring Boot에서 Redis Cache 사용  (0) 2024.01.06
Redis 데이터 타입과 기본 명령어  (0) 2024.01.05

+ Recent posts