- 浏览: 1011291 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (826)
- 硬件 (8)
- 软件 (24)
- 软件工程 (34)
- JAVA (229)
- C/C++/C# (77)
- JavaScript (8)
- PHP (1)
- Ruby (3)
- MySQL (14)
- 数据库 (19)
- 心情记事 (12)
- 团队管理 (19)
- Hadoop (1)
- spring (22)
- mybatis(ibatis) (7)
- tomcat (16)
- velocity (0)
- 系统架构 (6)
- JMX (8)
- proxool (1)
- 开发工具 (16)
- python (10)
- JVM (27)
- servlet (5)
- JMS (26)
- ant (2)
- 设计模式 (5)
- 智力题 (2)
- 面试题收集 (1)
- 孙子兵法 (16)
- 测试 (1)
- 数据结构 (7)
- 算法 (22)
- Android (11)
- 汽车驾驶 (1)
- lucene (1)
- memcache (12)
- 技术架构 (7)
- OTP-Erlang (7)
- memcached (17)
- redis (20)
- 浏览器插件 (3)
- sqlite (3)
- Heritrix (9)
- Java线程 (1)
- scala (0)
- Mina (6)
- 汇编 (2)
- Netty (15)
- libevent (0)
- CentOS (12)
- mongod (5)
- mac os (0)
最新评论
-
kingasdfg:
你这里面存在一个错误添加多个任务 应该是这样的 /** * ...
Quartz的任务的临时启动和暂停和恢复【转】 -
kyzeng:
纠正一个错误,long型对应的符号是J,不是L。
Jni中C++和Java的参数传递 -
zhaohaolin:
抱歉,兄弟,只是留下作记录,方便学习,如果觉得资料不好,可以到 ...
netty的个人使用心得【转】 -
cccoooccooco:
谢谢!自己一直以为虚机得使用网线才可以与主机连接呢。。
主机网卡无网线连接与虚拟机通信 -
yuqilin001:
要转别人的东西,请转清楚点嘛,少了这么多类,误人子弟
netty的个人使用心得【转】
In my previous article
, I took a
superficial look at how Redis starts up and prepares itself to process
commands. In this article, I'll follow a GET
and a SET
command as
they move from client through the server and back. The GET
will be for
a key that doesn't exist, and the SET
will set that key. Then I'll look
quickly at a subsequent GET
and how it differs.
As before, I'm exploring Redis with the source code open in my editor and
indexed with a tags
file, and the Redis server running under gdb
in
another terminal.
Caveat: this article was written against the codebase of Redis 2.2.1. With respect to my previous article, for a list of what has changed in Redis since I wrote it, see this comment on HN .
Edit:
I made two minor changes based on feedback—Redis keys are not Redis
objects, they are sds
strings; and you don’t have to hack the Makefile to
compile without optimizations.
GET
Let's execute the command get users:1234
on Redis.
Preparing
If you inspect certain variables under gdb
, you might not get what you want.
Instead you see a message like "." This is because the compiler has optimized the machine code
in such a fashion that the portion of memory you wanted to look at that was
never used, at least for the variable under inspection. To make stepping
through the code and inspecting a little easier, we make sure that Redis is
not compiled with optimizations. You can do this by either building Redis with
the following invocation:
make noopt
or by setting an environment variable:
OPTIMIZATION = make
Sending the command from the client
If we look at the repl
loop of redis-cli, we see that it uses the
linenoise
library to split the arguments of the command. It dispatches on
the first argument. The loop checks for client commands that aren't processed
as a command by the Redis server, like exit
/quit
, clear
(to clear the screen),
and connect
(which is a way to connect to a specified Redis server on host
port, instead of the default host of 127.0.0.1 and port of 6379.
Any other argument is considered the name of a command to send to the server. The remaining arguments are the arugments for that command.
We're trying to get the on-the-wire representation of the get users:1234
command.
redis-cli uses a redisContext
struct to encapsulate the state of the
connection to the server, as well as the output buffer where the command in
the form of the Redis protocol is written for sending to the server. We know
from reading the source of hiredis (the Redis C client library that the redis-cli
program is built on) that the obuf
field is where the raw command is stored:
# hiredis.h:107 /* Context for a connection to Redis */ typedef struct redisContext { int fd ; int flags ; char * obuf ; /* Write buffer */ int err ; /* Error flags, 0 when there is no error */ char * errstr ; /* String representation of error when applicable */ /* Function set for reply buildup and reply reader */ redisReplyObjectFunctions * fn ; void * reader ; } redisContext ;
If we set a breakpoint on cliReadReply
, we can inspect the output buffer and
see exactly how the command looks as a bytestring bound for the server.
client $ gdb src/redis-cli (gdb) b cliReadReply (gdb) run Starting program: /home/paul/src/redis-2.2.0-RC2/src/redis-cli Reading symbols for shared libraries +. done redis> get users:1234 Breakpoint 1, cliReadReply (output_raw_strings=0) at redis-cli.c:421 421 if (redisGetReply(context,&_reply) != REDIS_OK) { (gdb) p context->obuf $1 = 0x100102428 "*2\r\n$3\r\nget\r\n$10\r\nusers:1234\r\n"
We see that the Redis command get users:1234
as entered in our client is
translated to *2\r\n$3\r\nget\r\n$10\r\nusers:1234\r\n
. Any Redis client is
going to convert our command expressed in its respective syntax to the same
on-the-wire format. So in Python:
>>> redis_client.get('users:1234')
will send the same *2\r\n$3\r\nget\r\n$10\r\nusers:1234\r\n
bytestring to
the server.
Let's print that bytestring to screen and render those `rn's as line feeds so we can see an expanded view and get a better look at the protocol.
*2 $3 get $10 users:1234
The first bit is *2
, which tells us that the arity of the command,
including the command name, is 2. That is, the server should expect two
arguments to follow.
The next bit is $3
, which means that the length of the first argument in
bytes is 3. The argument itself follows, our command name, get
.
The next bit after that is $10
, so the length in bytes of the second
argument is 10. Our one and only argument to the command is next,
users:1234
, the key we are trying to get
.
Receiving the command on the server
If you remember from the last article, readQueryFromClient
is a good place
to set a breakpoint in your debugger on the server side for inspecting an
inbound client command.
server $ gdb src/redis-server (gdb) b readQueryFromClient Breakpoint 1 at 0x100011445: file networking.c, line 882. (gdb) run redis.conf Starting program: /home/paul/src/redis-2.2.0-RC2/src/redis-server redis.conf Reading symbols for shared libraries +. done [63700] 01 Mar 11:04:40 * Server started, Redis version 2.2.1 [63700] 01 Mar 11:04:40 * The server is now ready to accept connections on port 6379
Now back in the terminal with the client running in the debugger, continue to send the command to the server, which will stop at the breakpoint we set.
# client (gdb) c Continuing. # server Breakpoint 1, readQueryFromClient (el=0x100200000, fd=5, privdata=0x100804e00, mask=1) at networking.c:801 801 void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
Let's step to the following line:
# src/networking.c:808 nread = read ( fd , buf , REDIS_IOBUF_LEN );
If we step to here in our debugger, we see that the server read 30 bytes. If
you count the number of bytes in our Redis protocol-encoded command,
*2\r\n$3\r\nget\r\n$10\r\nusers:1234\r\n
, you'll see it's 30. Just for good
measure, let's look at the 30 bytes beginning at the memory location pointed
to by buf
:
(gdb) p nread 30 (gdb) x/30cb buf 0x7fff5fbfeaf0: 42 '*' 50 '2' 13 '\r' 10 '\n' 36 '$' 51 '3' 13 '\r' 10 '\n' 0x7fff5fbfeaf8: 71 'G' 69 'E' 84 'T' 13 '\r' 10 '\n' 36 '$' 49 '1' 48 '0' 0x7fff5fbfeb00: 13 '\r' 10 '\n' 117 'u' 115 's' 101 'e' 114 'r' 115 's' 58 ':' 0x7fff5fbfeb08: 49 '1' 50 '2' 51 '3' 52 '4' 13 '\r' 10 '\n'
And we can see our whole command bytestring is there, byte by byte.
The server has now read in the entirety of our command in one step. (Because
we had a relatively short command, one that fits inside a kernel buffer, and
we are the only client on a loopback network device, this is the case, but it
need not be. Since Redis is event-driven, this function,
readQueryFromClient
, is called whenever there are bytes from buffers to be
read. If our command was particularly long, or there was a lot of network
contention, the command may take more than one I/O event before it is fully
read. For this reason, Redis builds up a buffer per client and appends bytes
to it on each call to this function. It only proceeds with processing the
command when it has been fully read. But we don't need to consider this in our
simple example, so we will proceed.)
We're going to elide the processing of the input buffer. This is the point
where the server takes the Redis protocol-encoded bytestring of our request
and unpacks it into arguments on the client struct object. If you are
interested in the details of that parsing, examine the function
processMultibulkBuffer
in networking.c
. All we are interested in at this
point is that the argc
member of the client object is the number of command
arguments (counting the command name itself) and argv
is a pointer to the
list of arguments.
The bit of code we care at this point is processCommand
. The first thing the
server does is look up the command in its command table (see "Setting up
command table" in the previous article, but note that this lookup is now O(1),
see the HN thread linked above). Assuming the command is found (which our
get
will be), the server will double-check that the arity of the command as
defined in the command table matches the number of arguments received from the
client (c->argc
).
# redis.c:998 cmd = lookupCommand ( c -> argv [ 0 ] -> ptr ); if ( ! cmd ) { addReplyErrorFormat ( c , "unknown command '%s'" , ( char * ) c -> argv [ 0 ] -> ptr ); return REDIS_OK ; } else if (( cmd -> arity > 0 && cmd -> arity != c -> argc ) || ( c -> argc < - cmd -> arity )) { addReplyErrorFormat ( c , "wrong number of arguments for '%s' command" , cmd -> name ); return REDIS_OK ; }
Skip down to the end of processCommand
. Because our humble get
is not a
"multi" command like mget
, mset
, etc., it doesn't require queue-like
processing of the underlying multiple commands, so we go right to call
,
which is where our command is dispatched.
# redis.c:953 void call ( redisClient * c , struct redisCommand * cmd ) { long long dirty ; dirty = server . dirty ; cmd -> proc ( c ); dirty = server . dirty - dirty ; if ( server . appendonly && dirty ) feedAppendOnlyFile ( cmd , c -> db -> id , c -> argv , c -> argc ); if (( dirty || cmd -> flags & REDIS_CMD_FORCE_REPLICATION ) && listLength ( server . slaves )) replicationFeedSlaves ( server . slaves , c -> db -> id , c -> argv , c -> argc ); if ( listLength ( server . monitors )) replicationFeedMonitors ( server . monitors , c -> db -> id , c -> argv , c -> argc ); server . stat_numcommands ++ ; }
Let's focus on line 952, cmd->proc(c);
. This is Redis's dynamic dispatching
of command function calling. Redis makes this clean and simple by
encapsulating commands and giving all the actual underlying command functions
the same function signature, taking our client object, which carries the
payload of our command's arguments. So we're interested in looking into the
details of the Redis command object and the actual function that will handle
our get
.
# redis.h:504 struct redisCommand { char * name ; redisCommandProc * proc ; int arity ; int flags ; /* Use a function to determine which keys need to be loaded * in the background prior to executing this command. Takes precedence * over vm_firstkey and others, ignored when NULL */ redisVmPreloadProc * vm_preload_proc ; /* What keys should be loaded in background when calling this command? */ int vm_firstkey ; /* The first argument that's a key (0 = no keys) */ int vm_lastkey ; /* THe last argument that's a key */ int vm_keystep ; /* The step between first and last key */ };
If we pop up to the top of redis.c
, we see the definition of the Redis command
table, and our get
is the first entry.
# redis.c:71 struct redisCommand readonlyCommandTable [] = { { "get" , getCommand , 2 , 0 , NULL , 1 , 1 , 1 },
getCommand
is the function that does the actual work for our command.
It's a thin wrapper for getGenericCommand
.
# t_string.c:62 int getGenericCommand ( redisClient * c ) { robj * o ; if (( o = lookupKeyReadOrReply ( c , c -> argv [ 1 ], shared . nullbulk )) == NULL ) return REDIS_OK ; if ( o -> type != REDIS_STRING ) { addReply ( c , shared . wrongtypeerr ); return REDIS_ERR ; } else { addReplyBulk ( c , o ); return REDIS_OK ; } } void getCommand ( redisClient * c ) { getGenericCommand ( c ); }
The arguments to lookupKeyReadOrReply
are the client object, the key
users:1234
we're trying to look up, and an object, shared.nullbulk
that
will be the default reply to the client if the key is not found.
# db.c:58 robj * lookupKeyReadOrReply ( redisClient * c , robj * key , robj * reply ) { robj * o = lookupKeyRead ( c -> db , key ); if ( ! o ) addReply ( c , reply ); return o ; }
lookupKeyRead
is a thin wrapper for lookupKey
that handles removing keys
that have been set to expire.
Now we get to the heart of the get
command -- looking up the key in the
database.
# db.c:9 robj * lookupKey ( redisDb * db , robj * key ) { dictEntry * de = dictFind ( db -> dict , key -> ptr ); if ( de ) { robj * val = dictGetEntryVal ( de ); /* Update the access time for the aging algorithm. * Don't do it if we have a saving child, as this will trigger * a copy on write madness. */ if ( server . bgsavechildpid == - 1 && server . bgrewritechildpid == - 1 ) val -> lru = server . lruclock ; if ( server . vm_enabled ) { if ( val -> storage == REDIS_VM_MEMORY || val -> storage == REDIS_VM_SWAPPING ) { /* If we were swapping the object out, cancel the operation */ if ( val -> storage == REDIS_VM_SWAPPING ) vmCancelThreadedIOJob ( val ); } else { int notify = ( val -> storage == REDIS_VM_LOADING ); /* Our value was swapped on disk. Bring it at home. */ redisAssert ( val -> type == REDIS_VMPOINTER ); val = vmLoadObject ( val ); dictGetEntryVal ( de ) = val ; /* Clients blocked by the VM subsystem may be waiting for * this key... */ if ( notify ) handleClientsBlockedOnSwappedKey ( db , key ); } } server . stat_keyspace_hits ++ ; return val ; } else { server . stat_keyspace_misses ++ ; return NULL ; } }
Redis uses its own hash table implementation to store keys and their values in
memory. Inside the db
object, the field dict
is a pointer to the hash
value for the current Redis database (remember there can be up to 16 separate
databases in a single Redis server instance, indexed by number).
First, Redis calls dictFind
with the database's hash table and a pointer
to the key's bytestring. dictFind
looks up the hash of the key in the
table, using a standard algorithm that should be familiar to anyone who's
implemented a hash table (check out dict.c
starting at line 391 if you're
interested, the table is an array with linked lists for colliding hashes).
If the key is found in the table, dictFind
returns a pointer to the entry in
the table. Otherwise, it returns NULL
. Back in lookupKey
, if the entry is
not null, Redis retrieves the value (i.e., the Redis object our key
references) from the hash table via dictGetEntryVal
and takes care of a bit
of bookkeeping for expiry and VM, if the key was found, and stats in either
case (hits and misses). If the entry was NULL
, then lookupKey
also returns
NULL
; we'll see how this is handled by Redis for a reply to the client when
the key is not found, which is the case for us at this stage.
With the value of lookupKey
, we'll go back up the stack to our callers. Back
to lookupKeyReadOrReply
, we look at line 60:
# t_string.c:60 if ( ! o ) addReply ( c , reply );
Since we got NULL
from lookupKey
this time, we call addReply
. The value
of reply
here comes from the call in getGenericCommand
, and it is
shared.nullbulk
. This field in the global struct object shared
is
initialize thusly:
# redis.c:712 shared . nullbulk = createObject ( REDIS_STRING , sdsnew ( "$-1 \r\n " ));
We can see that it is a Redis string object who's on-the-wire value is
$-1\r\n
, meaning a length of -1, Redis's way of indicating null to a
client, according to the protocol.
addReply
builds the reply to the client. It does this by first setting up a
write event on the main event loop listener with _installWriteEvent
. This
makes sure that the reply is written out to the client connection when there
are bytes present in the buffer. Next, Redis adds the reply to the client's
buffer. If the reply object were an non-string value like an integer, or a
list, or a set, Redis would first decode it to a bytestring that can be
serialized to, for example, on a socket. Redis string objects are encoded
"raw," or as-is. The nullbulk
object is technically a string object, so no
decoding is necessary in our case. In any case, the reply bytestring is copied
to the client's reply buffer with _addReplyToBuffer
, which for all intents
and purposes completes the execution of our get
command on the server.
The client will read the on-the-wire reply of $-1\r\n
and know that it is a
string reply of length -1, and therefore is the null (or "nil," in the context
of redis-cli
) object, and to convert that into the appropriate object for
the language of the client. Back to our redis-cli
client patiently waiting
for a reply from our breakpointed server, which we continue from, that looks
like:
(nil) redis>
SET
The set
command proceeds much the same way as the get
, up to the point of
command dispatching on the server.
# client redis> set users:1234 "Paul Smith" # server Breakpoint 1, readQueryFromClient (el=0x100400000, fd=6, privdata=0x100805e00, mask=1) at networking.c:801 801 void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) { (gdb) n 802 redisClient *c = (redisClient*) privdata; (gdb) n 808 nread = read(fd, buf, REDIS_IOBUF_LEN); (gdb) n 809 if (nread == -1) { (gdb) print nread $1 = 47 (gdb) x/47cb buf 0x7fff5fbfeba0: 42 '*' 51 '3' 13 '\r' 10 '\n' 36 '$' 51 '3' 13 '\r' 10 '\n' 0x7fff5fbfeba8: 115 's' 101 'e' 116 't' 13 '\r' 10 '\n' 36 '$' 49 '1' 48 '0' 0x7fff5fbfebb0: 13 '\r' 10 '\n' 117 'u' 115 's' 101 'e' 114 'r' 115 's' 58 ':' 0x7fff5fbfebb8: 49 '1' 50 '2' 51 '3' 52 '4' 13 '\r' 10 '\n' 36 '$' 49 '1' 0x7fff5fbfebc0: 48 '0' 13 '\r' 10 '\n' 80 'P' 97 'a' 117 'u' 108 'l' 32 ' ' 0x7fff5fbfebc8: 83 'S' 109 'm' 105 'i' 116 't' 104 'h' 13 '\r' 10 '\n' (gdb) print (char *)buf $2 = 0x7fff5fbfeba0 "*3\r\n$3\r\nset\r\n$10\r\nusers:1234\r\n$10\r\nPaul Smith\r\n"
This time, our protocol-encoded bytestring is 47 bytes long, owing to the
extra argument "Paul Smith" and the length tag it requires. Also notice the
leading *3
indicates there are three arguments: set
, users:1234
, Paul Smith
.
Let's skip ahead now to the point in call
in redis.c
, after the command has
been looked-up in the command table and the server is about ready to call the
underlying proc
function with the client object argument. The set
command,
in the form of a redisCommand
struct, looks like this:
# redis.c:73 { "set" , setCommand , 3 , REDIS_CMD_DENYOOM , NULL , 0 , 0 , 0 },
Notice that the arity of the command is 3, which includes the leading command
name, plus key and value, and matches what we expect from the client. The
set
command has a flag that get
did not: the constant REDIS_CMD_DENYOOM
means that, in out-of-memory situations where Redis can't allocate any more
memory, the execution of the command should be denied. (The absence of this
flag means that Redis can continue to serve client "read" requests like get
even when the server can no longer write any new data.)
I set a breakpoint on setCommand
and let the server continue running until
that point:
# server (gdb) b setCommand Breakpoint 2 at 0x10001a6e2: file t_string.c, line 48. (gdb) c Continuing. Breakpoint 2, setCommand (c=0x100805e00) at t_string.c:48 48 c->argv[2] = tryObjectEncoding(c->argv[2]);
Incidentally, you can inspect the values of the client's command arguments at
any time, with a simple gdb
invocation. The arguments are of type robj
,
which has a field ptr
that is a pointer to the actual value in memory. Since
in our set
case these are strings, we can inspect them by typecasting to
char *
like so:
(gdb) p (char *)c->argv[0]->ptr $10 = 0x10032ae78 "set" (gdb) p (char *)c->argv[1]->ptr $11 = 0x10032b068 "users:1234" (gdb) p (char *)c->argv[2]->ptr $12 = 0x10032b098 "Paul Smith"
The first thing the server does in setCommand
is encode the value being set
with tryObjectEncoding
. It will try to create an efficient encoding if the
bytestring can be interpreted as an integer, for example. This can save space
especially in the case where many numbers are being stored. Additionally,
Redis will try to reuse shared integers as values instead of allocating
resources for new ones -- see the previous article for more on the creation
and use of shared integers.
Once the value being set has been encoded, setGenericCommand
is called
(set
shares setGenericCommand
with the setnx
and setex
commands). From
here, dbAdd
is called, with the client, key, and value as arguments. dbAdd
will only add the value to the database's hash table if the key does not
already exist. In our case, since the key users:1234
does not exist, the
value of dictFind
is null, and the function proceeds to add the value with
dictAdd
.
dictAdd
takes the dictionary hash table, key, and value as arguments. It
uses _dictKeyIndex
to find the index of a free slot in the hash table for
our new entry. See the implementation in dict.c
and dict.h
for the details
of key hashing, and the structure of the dictionary and its component hash
tables (each Redis dictionary contains two hash tables in order to provide
incremental rehashing as the dictionary grows). dictAdd
allocates memory for
the new entry and stores it in the new index.
The server returns back up the stack from dictAdd
and dbAdd
to
setGenericCommand
, where it increments the reference count on our new value.
Redis uses reference counting in order to free memory used by values that have
been deleted or have expired. It then "touches" the key so that if any clients
are watch
ing the key, the next exec
command will fail. It also increments
the server's dirty
flag, which it uses to determine when to write out the
dump file to disk. Finally, it writes out the reply to the client, which is a
shared object, shared.ok
. This is special Redis string object in the
protocol that consists of the bytestring "+OKrn". Clients will typically
convert this into the equivalent "true" value for their language.
GET redux
Our key is now set, so we can try the get users:1234
command again and see
how it differs for a found key.
# db.c:9 robj * lookupKey ( redisDb * db , robj * key ) { dictEntry * de = dictFind ( db -> dict , key -> ptr ); if ( de ) { robj * val = dictGetEntryVal ( de ); // ... skipping the lru & vm parts ... server . stat_keyspace_hits ++ ; return val ;
The point where a get
on an existing key and a get
on a non-existent key
differ is line 11, where the de
entry in the database's dictionary is found.
dictGetEntryVal
is a simple macro for accessing the field in the de
struct
that carries the value associated with the key. Redis updates its statistics
to indicate a key hit and returns the value object.
Again, as with the key miss from above (remember the null value is a Redis
object, too), the value is decoded into the Redis bytestring protocol. This is
the response to the client, and we have concluded our GET
/SET
/GET
dance.
发表评论
-
Redis入门教程
2011-07-12 22:08 2158入门教程,仅供菜鸟。 ... -
Redis数据库介绍
2011-07-12 22:07 28171. Redis是什么 这个问题的结果影响了我们怎么用R ... -
redis-sharding – 一个 Redis Proxy【转】
2011-04-28 01:43 1647redis-sharding 是一个由perl写的 Redis ... -
redis 运维实际经验纪录之一【转】
2011-04-26 11:30 1181发表于 2010/05/25 由 ... -
1,250,000,000 Key/Value Pairs in Redis 2.0.0-rc3 on a 32GB Machine【转】
2011-04-26 11:28 910Posted on July 25, 20 ... -
200,000,000 Keys in Redis 2.0.0-rc3【转】
2011-04-26 11:27 978Posted on July 24, 20 ... -
Redis配置文件各项参数说明【转】
2011-04-26 11:00 1624最近一个项目用到Redis ... -
Redis配置主从服务器【转】
2011-04-26 10:31 1271redis配置主从服务器 R ... -
深入Redis内部-Redis 源码讲解【转】
2011-04-26 10:08 604Redis 作为 NoSQL 数据库的杰出代表,一直广受关注, ... -
redis安装配置及使用【转】
2011-04-26 09:03 15672011-03-27 20:41 http://cod ... -
Redis安装配置【转】
2011-04-26 09:02 2044http://bbs.linuxtone.org/thread ... -
闲聊Redis【转】
2011-04-26 09:00 1157Redis 是一个有趣 的项目,与其把它说成键值存储、键 ... -
redis知识点整理【转】
2011-04-25 22:40 1062xmind做的笔记,可惜只能截图 关键字: ... -
jedis线程池的代码【转】
2011-04-25 22:36 2800备忘:(本机i5的测试数据:redis线程执行时间是:2118 ... -
在多台服务器上简单实现Redis的数据主从复制【转】
2011-04-25 22:27 1215Redis的主从复制功能非常强大,一个mas ter可以拥有 ... -
归档Redis在Windows,linux平台下的安装配置【转】
2011-04-25 22:25 2212为了方便查阅资料,特将网上搜索到的Redis相关安装配置进行归 ... -
Redis在Windows下的使用[转]
2011-04-25 22:24 1155Windows版的Redis可到此处下载,非官方版http:/ ... -
windows下redis和memcached的性能对比测试【转】
2011-04-25 22:19 1698在windows下测试了redis和memcached的性能, ... -
膜拜Redis【转】
2011-04-25 21:14 984膜拜Redis 2009年06月18 ...
相关推荐
重要请注意,该库不再维护。 有关新更新,请参阅以下分支 我已经对该库进行了3年以上的维护...client.get( " hello " , [](cpp_redis::reply& reply) { std::cout << reply << std::endl;});// ! also sup
cpp_redis cpp_redis是C ++ 11异步多平台轻量级Redis客户端,支持同步操作,流水线,标记和高可用性。需求cpp_redis没有依赖项。 它唯一的要求是C++11 。 它没有网络模块,因此您可以自由配置自己的模块,也可以使用...
在IT行业中,Redis是一款广泛应用的高性能键值存储系统,它以速度快、数据持久化和丰富的数据结构而闻名。而C++作为一门强大的编程语言,与Redis的结合使得开发者能够高效地利用Redis的功能。本篇文章将深入探讨如何...
window系统上使用redis 压缩包里有64位和32位的,请选择解压。 解压到E盘根目录(解压目录随你设置) 打开cmd窗口,进入redis根目录 ...redis 127.0.0.1:6379> set name tom OK redis 127.0.0.1:6379> get name "tom
《Redis深度历险:核心原理和应用实践》是一本由钱文品老师编著的专业书籍,旨在深入探讨Redis这一流行的数据存储系统的核心机制和实际应用场景。这本书不仅涵盖了Redis的基础知识,还深入到其高级特性和最佳实践,...
Redis命名空间Redis的::命名空间提供了一个接口,您的命名空间的子集密钥空间(例如,具有共同的开始键),并要求宝石。 require 'redis-namespace'# => trueredis_connection = ... set ( 'foo' , 'bar' ) # redis_
在Linux环境中,Redis是一个广泛应用的开源键值存储系统,常被用作数据库、缓存以及消息中间件。本文将详细讲解如何在Linux上安装Redis,包括Redis 4.0.2和5.0.0两个版本,以及如何配合使用Redis Desktop Manager...
Redis 安装菜鸟教程 Redis 是一个开源的基于内存的数据结构存储系统,可以用作数据库、缓存和消息代理。以下是 Redis 安装的详细教程: Windows 下安装 1. 下载 Redis 安装包:从 ...
Redis安装实战 本文将从Redis的安装、配置、启动、内存分配策略等多方面进行详细介绍,为读者提供一个全面的Redis实践手册。 一、Redis简介 Redis是一种高级key-value数据库,它跟memcached类似,不过数据可以...
RedisPool Redis的连接池 依存关系 RedisPool只需要hiredis 安装 在Debian系统上安装hiredis: sudo apt-get install libhiredis-dev 或手动: git clone https://github.com/redis/hiredis.git cd hiredis & ...
4. **命令行工具集成**:内置了Redis命令行工具,可以直接执行各种Redis命令,如`GET`、`SET`、`FLUSHDB`等。 5. **事务与发布/订阅**:支持Redis的事务操作以及发布/订阅功能,方便测试和调试。 6. **实时监控**:...
redis 连接报错 GET_LIKE_ERROR 处理过程.rar
例如,在一次基准测试中,Redis的`set`操作可以达到每秒110,000次,`get`操作可以达到每秒81,000次。这样的性能对于大多数高并发的应用场景来说都是非常理想的。 #### 三、Redis与Memcached的主要区别 Redis与...
3. **发送命令**:使用 `send` 或 `sendSync` 方法发送 Redis 命令,例如 `GET`, `SET`, `INCR` 等。 4. **接收响应**:异步操作会返回一个 `Future` 对象,你可以通过 `get` 方法获取结果,同步操作则直接返回结果...
## Set the connection name (requires Redis 2.6.9) my $redis = Redis::Fast->new( server => 'redis.example.com:8080', name => 'my_connection_name', ); my $generation = 0; my $redis = Redis::Fast->new...
安装 Redis:sudo apt-get install redis-server 启动 Redis:/usr/bin/redis-server /etc/redis/redis.conf 重启 Redis:/etc/init.d/redis-server restart PHP-Redis 扩展是 PHP 的一个扩展,效率相当高,拥有...
Redis命令实践 目录: 1. 引言 2. Redis简介 3. 安装和配置 4. 基本命令 5. 示例代码 6. 结论 7. 参考文献 引言: Redis是一个开源的内存数据结构存储系统,常用于缓存、消息队列等场景。本文将介绍Redis的基本...
Redis命令实践 目录: 1. 引言 2. Redis简介 3. 安装和配置 4. 基本命令 5. 示例代码 6. 结论 7. 参考文献 引言: Redis是一个开源的内存数据结构存储系统,常用于缓存、消息队列等场景。本文将介绍Redis的基本...
Redis命令实践 目录: 1. 引言 2. Redis简介 3. 安装和配置 4. 基本命令 5. 示例代码 6. 结论 7. 参考文献 引言: Redis是一个开源的内存数据结构存储系统,常用于缓存、消息队列等场景。本文将介绍Redis的基本...