Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the reason why Redis single thread can support high concurrency?

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)05/31 Report--

This article introduces the relevant knowledge of "what is the reason why Redis single thread can support high concurrency". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

Several Icano models

Why do you use this technique in Redis such as Iram O multiplexing?

First of all, Redis runs in a single thread, and all operations are performed linearly in order, but because the read and write operations wait for user input or output are blocked, so the Imax O operation can not be returned directly in general, which will lead to a file's Imax O blocking so that the whole process can not provide services to other customers, and Imax O multiplexing is to solve this problem.

Blocking I/O

Let's take a look at how the traditional blocking Redis O model works: when you use read or write to read and write to a * * file descriptor (File Descriptor hereinafter referred to as FD) * *, if the current FD is not readable or writable, the entire Redis service will not respond to other operations, making the entire service unavailable.

This is, in the traditional sense, the blocking model that we use most in programming:

Blocking-io

Although the blocking model is very common and easy to understand in development, because it affects other FD corresponding services, it is often not used when dealing with multiple client tasks.

Ipaw O Multiplexing

Although there are many other Istroke O models, none of them will be described in detail here.

The blocking Istroke O model does not meet the requirements here. We need a more efficient IMab O model to support multiple customers (redis-cli) of Redis. Here, we refer to the Imax O multiplexing model:

I:O-Multiplexing-Mode

In the Ipaw O multiplexing model, the most important function call is select, which can monitor the readability and writeability of multiple file descriptors at the same time. When some of the file descriptors are readable or writable, the select method returns the number of readable and writable file descriptors.

With regard to the specific use of select, there is a lot of information on the Internet, but there is no more introduction here.

At the same time, there are other epoll/kqueue/evport O multiplexing functions, which have better performance than select and can support more services.

Reactor design pattern

Redis services use Reactor to implement file event handlers (each network connection actually corresponds to a file descriptor)

Redis-reactor-pattern

The file event handler uses the IramO multiplexing module to listen to multiple FD at the same time, and when accept, read, write, and close file events are generated, the file event handler calls back the event handler bound by the FD.

Although the whole file event processor runs on a single thread, through the introduction of the Icano multiplexing module, it realizes the monitoring of multiple FD reads and writes at the same time, improves the performance of the network communication model, and ensures the simplicity of the implementation of the whole Redis service.

Ipaw O multiplexing module

The Istroke O multiplexing module encapsulates the underlying select, epoll, avport and kqueue functions, providing the same interface for the upper layer.

Ae-module

Here we briefly introduce how Redis wraps select and epoll, and briefly understand the function of this module. The whole Icano multiplexing module smoothes the difference of Icano multiplexing functions on different platforms and provides the same interface:

Static int aeApiCreate (aeEventLoop * eventLoop)

Static int aeApiResize (aeEventLoop * eventLoop, int setsize)

Static void aeApiFree (aeEventLoop * eventLoop)

Static int aeApiAddEvent (aeEventLoop * eventLoop, int fd, int mask)

Static void aeApiDelEvent (aeEventLoop * eventLoop, int fd, int mask)

Static int aeApiPoll (aeEventLoop * eventLoop, struct timeval * tvp)

At the same time, because the parameters required by each function are different, we use an aeApiState to store the required context information within each sub-module:

/ / select typedef struct aeApiState {fd_set rfds, wfds; fd_set _ rfds, _ wfds;} aeApiState; / / epoll typedef struct aeApiState {int epfd; struct epoll_event * events;} aeApiState

This context information will be stored in eventLoop's void * state and will not be exposed to the upper layer, but will only be used in the current sub-module.

Encapsulate select function

Select can monitor FD for readability, writeability, and errors.

Before introducing how the select function is encapsulated by the Ipaw O multiplexing module, let's take a look at the general flow of using the select function:

Int fd = / * file descriptor * / fd_set rfds; FD_ZERO (& rfds); FD_SET (fd, & rfds) for (;;) {select (fd+1, & rfds, NULL, NULL, NULL); if (FD_ISSET (fd, & rfds)) {/ * file descriptor `fd` becomes readable * /}}

Initialize a readable fd_set collection and save the FD that needs to monitor readability

Use FD_SET to add fd to rfds

Call the select method to monitor whether the FD in rfds is readable

When the select returns, check the status of the FD and complete the corresponding operation.

The order of organizing the code in the ae_select file of Redis is similar. First, initialize rfds and wfds in the aeApiCreate function:

Static int aeApiCreate (aeEventLoop * eventLoop) {aeApiState * state = zmalloc (sizeof (aeApiState)); if (! state) return-1; FD_ZERO (& state- > rfds); FD_ZERO (& state- > wfds); eventLoop- > apidata = state; return 0;}

AeApiAddEvent and aeApiDelEvent modify the corresponding FD flag bits in fd_set through FD_SET and FD_CLR:

Static int aeApiAddEvent (aeEventLoop * eventLoop, int fd, int mask) {aeApiState * state = eventLoop- > apidata; if (mask & AE_READABLE) FD_SET (fd,&state- > rfds); if (mask & AE_WRITABLE) FD_SET (fd,&state- > wfds); return 0;}

The most important function in the whole ae_select sub-module is aeApiPoll, which is the part that actually calls the select function. Its function is to add the corresponding FD to the fired array of aeEventLoop and return the number of events when the IWeiO multiplexing function returns:

Static int aeApiPoll (aeEventLoop * eventLoop, struct timeval * tvp) {aeApiState * state = eventLoop- > apidata; int retval, j, numevents = 0; memcpy (& state- > _ rfds,&state- > rfds,sizeof (fd_set)); memcpy (& state- > _ wfds,&state- > wfds,sizeof (fd_set)); retval = select (eventLoop- > maxfd+1, & state- > _ rfds,&state- > _ wfds,NULL,tvp) If (retval > 0) {for (j = 0; j maxfd; jacks +) {int mask = 0; aeFileEvent * fe = & eventLoop- > events [j]; if (fe- > mask = = AE_NONE) continue; if (fe- > mask & AE_READABLE & & FD_ISSET (jMed statestate-> _ rfds) mask | = AE_READABLE If (fe- > mask & AE_WRITABLE & & FD_ISSET (jlemminstate-> _ wfds)) mask | = AE_WRITABLE; eventLoop- > Fredevents [numevents] .fd = j; eventLoop- > fired[ numevents] .mask = mask; numevents++;}} return numevents; encapsulates the epoll function

The encapsulation of epoll by Redis is similar, using epoll_create to create the epfd used in epoll:

Static int aeApiCreate (aeEventLoop * eventLoop) {aeApiState * state = zmalloc (sizeof (aeApiState)); if (! state) return-1; state- > events = zmalloc (sizeof (struct epoll_event) * eventLoop- > setsize); if (! state- > events) {zfree (state); return-1;} state- > epfd = epoll_create (1024) / * 1024 is just a hint for the kernel * / if (state- > epfd = =-1) {zfree (state- > events); zfree (state); return-1;} eventLoop- > apidata = state; return 0;}

Use epoll_ctl in aeApiAddEvent to add the FD to be monitored and the events to be monitored to the epfd:

Static int aeApiAddEvent (aeEventLoop * eventLoop, int fd, int mask) {aeApiState * state = eventLoop- > apidata; struct epoll_event ee = {0}; / * avoid valgrind warning * / / * If the fd was already monitored for some event, we need a MOD * operation. Otherwise we need an ADD operation. * / int op = eventLoop- > events [FD] .mask = = AE_NONE? EPOLL_CTL_ADD: EPOLL_CTL_MOD; ee.events = 0; mask | = eventLoop- > events [FD] .mask; / * Merge old events * / if (mask & AE_READABLE) ee.events | = EPOLLIN; if (mask & AE_WRITABLE) ee.events | = EPOLLOUT; ee.data.fd = fd; if (epoll_ctl (state- > epfd,op,fd,&ee) =-1) return-1; return 0;}

Because the epoll mechanism is slightly different from the select mechanism, you do not need to traverse all the FD to check the read and write situation when the epoll_wait function returns; an epoll_event array is provided when the epoll_wait function returns:

Typedef union epoll_data {void * ptr; int fd; / * file descriptor * / uint32_t u32; uint64_t u64;} epoll_data_t; struct epoll_event {uint32_t events; / * Epoll event * / epoll_data_t data;}

It holds the epoll events that occurred (EPOLLIN, EPOLLOUT, EPOLLERR, and EPOLLHUP) and the FD in which the event occurred.

The aeApiPoll function only needs to add the information stored in the epoll_event array to the fired array of eventLoop and pass the information to the upper module:

Static int aeApiPoll (aeEventLoop * eventLoop, struct timeval * tvp) {aeApiState * state = eventLoop- > apidata; int retval, numevents = 0; retval = epoll_wait (state- > epfd,state- > events,eventLoop- > setsize, tvp? (tvp- > tv_sec*1000 + tvp- > tv_usec/1000):-1); if (retval > 0) {int j; numevents = retval; for (j = 0; j)

< numevents; j++) { int mask = 0; struct epoll_event *e = state->

Events+j; if (e-> events & EPOLLIN) mask | = AE_READABLE; if (e-> events & EPOLLOUT) mask | = AE_WRITABLE; if (e-> events & EPOLLERR) mask | = AE_WRITABLE; if (e-> events & EPOLLHUP) mask | = AE_WRITABLE; eventLoop- > fired.fd = e-> data.fd; eventLoop- > fired.mask = mask } return numevents;} selection of sub-module

Because Redis needs to run on multiple platforms, and in order to maximize the efficiency and performance of execution, different IWeiO multiplexing functions will be selected as sub-modules according to the compilation platform to provide a unified interface for the upper layer. In Redis, we reasonably select different sub-modules through the use of macro definitions:

# ifdef HAVE_EVPORT # include "ae_evport.c" # else # ifdef HAVE_EPOLL # include "ae_epoll.c" # else # ifdef HAVE_KQUEUE # include "ae_kqueue.c" # else # include "ae_select.c" # endif # endif # endif

Because the select function, as a system call in the POSIX standard, is implemented on different versions of the operating system, it is used as a guaranteed solution:

Redis-choose-io-function

Redis will give priority to the low-level implementation of the time-complex Icano multiplexing function, including evport in Solaries 10, epoll in Linux, and kqueue in macOS/FreeBSD, all of which use internal kernel structures and can serve hundreds of thousands of file descriptors.

However, if the current compilation environment does not have the above functions, select will be chosen as an alternative, because it will scan all listening descriptors when it is used, so its time complexity is poor, and it can only serve 1024 file descriptors at the same time, so select is not generally used as the first solution.

Summary

The design of Redis is very simple, which ensures the excellent performance of Icando multiplexing module on different platforms through macros, and provides the same API to the upper layer by encapsulating different Icano multiplexing functions.

The whole module enables Redis to serve thousands of file descriptors while running in a single process, avoiding the increase in code implementation complexity caused by the introduction of multi-process applications and reducing the possibility of errors.

This is the end of the content of "what is the reason why Redis single thread can support high concurrency". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Database

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report