首页 > 数据库 >详解Redis终端操作与Redis-plus-plus接口使用教程

详解Redis终端操作与Redis-plus-plus接口使用教程

来源:互联网 2026-07-07 09:13:01

Redis简介:内存数据库的核心概念与经典应用场景 我们先从几个核心概念说起。相比MySQL,Redis也可以当作数据库来用,而且快得多——因为它把所有数据都塞在内存里。但内存空间远小于硬盘,所以存储量有限,功能也比较“克制”,比如不支持多表查询这类操作。 这就引出了经典的“二八原则”:20%的数据

Redis简介:内存数据库的核心概念与经典应用场景

我们先从几个核心概念说起。相比MySQL,Redis也可以当作数据库来用,而且快得多——因为它把所有数据都塞在内存里。但内存空间远小于硬盘,所以存储量有限,功能也比较“克制”,比如不支持多表查询这类操作。

详解Redis终端操作与Redis-plus-plus接口使用教程

长期稳定更新的攒劲资源: >>>点此立即查看<<<

这就引出了经典的“二八原则”:20%的数据扛起了80%的访问需求。典型的做法是Redis搭配MySQL,用Redis缓存热点数据,剩下的数据老老实实放在MySQL里,只在需要时去取。当然,这样做的代价是系统复杂度上去了,还得解决Redis和MySQL之间的数据同步问题。

说到Redis的初衷,它最初是想当消息队列用的(也就是分布式系统里的生产者-消费者模型)。另外,MySQL的主从分离和读写分离大家也熟悉:读请求远多于写请求,所以只有主库处理写操作,然后把变更同步给从库,从库只负责读。上层再搞个负载均衡,把读请求分散到各个从节点上。

Redis存放、获取键值的操作

1. 存放键值对

在Redis里,key始终是字符串类型,这点得记牢。存放键值对直接用set keyname keyvalue就好,Redis不区分大小写,也不严格要求引号——写不写都行。

更多玩法:set keyname value [ex seconds | px milliseconds] [nx|xx]。ex指定超时时间(秒),px是毫秒;nx表示键不存在才插入,xx表示键存在时才修改。

批量操作:mset keyname value [keyname value],一次设置多组键值对。

还有几个快捷指令:setnx(不存在才设)、setex(设过期时间,秒)、psetex(设过期时间,毫秒)。

2. 获取键值对

取值用get keyname,批量取值用mget

获取键值对时的匹配原则

  • 匹配任意单个字符,比如ke能找出key。
  • * 匹配任意多个字符,用法和Linux文件通配符一样。
  • [abcdefg] 匹配中括号中的任一字符。
  • [^e] 排除字符e。
  • [a-c] 范围匹配。

不过,实际开发中几乎没人用*来扫全库的key——因为Redis是单线程的,这么做会导致线程卡在检索上,大量请求拿不到缓存数据,一股脑涌向数据库,数据库很容易被压垮。

3. 查看键值对是否存在

exists keyname1 keyname2 ...一次检查多个key是否存在。为什么Redis支持一次带多个key?因为Redis是客户端-服务器架构,每次请求都要走网络,网络传输和协议封装解包很耗时,所以干脆一次多带几个,省掉多次往返的延迟。

4. 删除键值对

删除用del keyname1 keyname2

5. 设置过期时间

  • expire keyname seconds – 秒为单位。
  • pexpire keyname microseconds – 毫秒为单位。

6. 查看存活时间

ttl keyname,返回剩余存活时间。没设过期时间返回-1,key不存在返回-2。

Redis怎样删除已经过期的key?

Redis采用两种策略结合:定期删除惰性删除。定期删除会定时抽一小部分key检查,因为Redis是单线程,不能花太多时间在这里,否则会阻塞。惰性删除则是在访问某个key时,发现它过期了,顺手就删掉。它没有用定时器来处理过期——不过定时器方案本身也值得了解,比如优先级队列和时间轮。

优先级队列

把有过期时间的任务按过期时间建成小根堆,线程根据堆顶的过期时间休眠,到点再唤醒。如果中间有新任务进来,就唤醒线程重新计算。

时间轮

用一个环形队列,每个格子指向一个链表头,链表里的任务过期时间落在某个范围内。检测线程定期走一格,处理该格中到期的任务。环形和周期叠加,一个链表中可能有不同时间的任务。时间轮对时间粒度和队列长度设计要求比较高。

7. 查看类型

type keyname 能查看key对应value的数据类型。

Redis数据类型与实现方式

Redis对外承诺了多种数据类型,但内部实现可能偷偷换了马甲。

1. string

  • raw – 普通字符串存储。
  • int – 如果字符串是数字,直接按整数存。
  • embstr – 对短字符串做了优化,也能存小数,但整数比较时就会慢一点,因为得先转成数字再比。

2. hash

  • hashtable – Redis自己实现的哈希表。
  • ziplist – 压缩列表。当哈希表元素很少时,用ziplist遍历也快,而且内存更紧凑。元素多了效率就下来了。

3. list

老版本用linkedlist + ziplist,Redis 3.2以后改为quicklist,类似C++的deque。

4. set

  • hashtable – 哈希表。
  • intset – 整数集合,数据少且全是整数时使用。

5. zset

  • skiplist – 跳表,每个节点有多个指针域,能实现O(log N)的查询。
  • ziplist – 元素少或元素体积小时使用。

查看具体存储方式

object encoding keyname 就能看到某个value实际采用了哪种底层结构。

Redis的单线程工作模型

客户端发请求是并发的,但Redis服务器处理核心逻辑是单线程的,所有请求串行执行,没有并发问题。所以如果一个操作阻塞太久,可能整个服务就挂了。反过来讲,Redis处理的业务都是短平快的,对并行需求很低,单线程完全够用。

为什么说Redis快,效率高?

首先,Redis快主要是和MySQL这类磁盘数据库比。内存操作比磁盘操作快好几个数量级,先天优势。其次,Redis功能精简,没有约束检查、多表联查这些复杂操作,省掉了大量额外开销。

string 类型

1. incr和incrby

  • incr – 给value加1,只有int类型才能用。
  • incrby – 加n,n可以是负数。key不存在就从0开始算。

2. decr和decrby

和incr系列相反,做减法。

3. incrbyfloat

支持小数加减,想减就传负数。

4. append

追加字符串,返回追加后的长度。注意:Redis不会对字符集做特殊处理,输入中文默认按原编码存储。可以在启动客户端时加--raw参数,让客户端试着把二进制数据转成可读的中文。

5. getrange

getrange keyname start end 取[start, end]区间的字节内容。因为按字节操作,中文可能被截成乱码(UTF-8或GBK一个汉字占多个字节)。end可以是负数,-1代表最后一个字符。如果start>end会取不到内容。

6. setrange

setrange key start value,从start位置开始覆盖为value,覆盖长度取决于value的长度。如果key不存在,会用空格填充到start位置。如果start超过了原内容长度,中间也用空格补齐。

7. strlen

返回字符串value的字节长度,中文按实际字节数算。

Redis中的hash类型

Redis的hash结构是field-value对,value只能是字符串。

1. hset、hget、hexists、hdel

  • hset keyname fieldname value – 设置哈希表中的字段。
  • hget keyname fieldname – 获取字段值。
  • hexists keyname fieldname – 判断字段是否存在。
  • hdel keyname fieldname – 删除字段。

2. hkeys、hvals

hkeys key获取所有field,hvals key获取所有value。慎用这两个命令,跟keys *一样可能阻塞Redis。

hlen、hsetnx、hincrby、hincrbyfloat

  • hlen keyname – 哈希表的大小。
  • hsetnx – 不存在则插入,存在则失败。
  • hincrby – 给某个value加n(数字类型)。
  • hincrbyfloat – 加小数。

使用哈希存储的场景

可以把哈希当作“对象”用,存储一个实体的多个属性,省去大量独立的key。更新某个字段时不用把整个数据读出来再写回去。另外,哈希有稀疏性——不需要的字段可以不存,而关系型数据库里即使没有值也得用NULL占位。

列表

列表类似顺序表,但底层用类似deque的容器,在头尾插入删除效率高。列表是有序的:元素相同但顺序不同就是两个不同的列表。

lpush lpop rpush rpop

分别对应push_frontpop_frontpush_backpop_back

lrem lindex lrange

  • lrem key count value – 删除列表中前count个等于value的元素。
  • lrange key start stop – 返回[start, stop]区间的元素(显示结果时前面的数字是序号,不是下标)。
  • lindex key index – 获取指定下标的元素。

lpushx 和 rpushx

只有列表存在时才插入,带x表示exist。

lpop 和 rpop

lpop keyname [count] 从左端删除count个(低版本可能不支持count参数),rpop同理。搭配lpush/rpush可以当栈或队列用。

linsert

linsert keyname [before|after] pivot value,在pivot值的前或后插入value,pivot从左到右找第一个。

lrem

lrem keyname count element:count>0从左到右删count个;count=0删所有等于element的;count<0从右到左删|count|个。

ltrim 和 lset

  • ltrim keyname start stop – 只保留[start, stop]区间,其余丢弃。
  • lset keyname index element – 将index位置设为element,超出范围会报错。

blpop 和 brpop

阻塞等待。Redis的阻塞是特殊的,不会影响核心逻辑。不会考虑队列满的情况,只考虑队列为空时阻塞等待。blpop keyname timeout,timeout=0表示一直等。可以同时等多个队列,从左到右第一个有数据的返回。

list列表的应用场景

时间轴、消息轴:用lpush插新内容,lrange分页,ltrim保留部分。消息队列:lpush + brpop就能搭一个阻塞队列。

set集合

set和list不同:set无序,且元素不能重复。

sadd smember sismember scard

  • sadd key elements – 添加元素。
  • smembers key – 获取所有元素。
  • sismember key member – 判断member是否在集合中。
  • scard keyname – 获取元素个数。

spop srandmember

  • spop keyname – 随机弹出一个元素并删除。
  • srandmember keyname – 随机返回一个元素,但不删除。

srem smove

  • srem keyname element – 删除指定元素。
  • smove source destination element – 把source中的element移到destination中。如果destination已存在该元素,操作成功但不会重复。

sinter sinternstore 交集

  • sinter set1 set2 ... – 求交集。
  • sinterstore dest set1 set2 ... – 求交集并存入dest。

sunion sunionstore sdiff sdiffstore

并集和差集,用法与交集相同。

set的应用场景

用户画像:用标签(小字符串)打标签。好友推荐:set求交集很方便。UV统计:UV需要去重,set正合适(PV是Page View,统计访问次数;UV是User View,统计独立用户数)。

zset

zset也是有序的,但它的“有序”是真正的排序——基于score。每个member关联一个score,类似pair。

zadd

zadd key [nx|xx] [gt|lt] [ch] [incr] score member [score member ...]

  • 默认返回新增元素个数;如果加了ch,返回被修改的元素个数。
  • gt/lt:当新score大于/小于原score时才更新。
  • nx/xx:只增/只改。
  • incr:像incrby一样操作score。

zrange

  • zrange key start end [withscore] – 展示元素。
  • zrevrange key start end [withscore] – 逆序展示。
  • zrangebyscore key start end [withscore] – 按score范围展示,start/end是score区间。

zcount

zcount key start end – 统计score区间内的member个数。用(表示开区间,比如(2 (5,支持-infinf

zcard

zcard key – 统计member个数。

zpopmax

zset默认升序排列,zpopmax会删除score最大的member并返回。可加count一次取多个,时间复杂度O(log N * M)。

bzpopmax

bzpopmax key [key...] timeout – 阻塞式取最大member。可同时等多个key,任一有数据就返回。

zpopmin bzpopmin

和zpopmax/bzpopmax相反,操作最小值。

zrank zrevrank zscore

  • zrank key member – 查询member从前往后的排名(类似数组下标)。
  • zrevrank key member – 从后往前的排名(最后一个元素下标为0)。
  • zscore key member – 查分数,Redis用空间换时间,这个操作是O(1)。

zrem zremrangebyrank zremrangebyscore

  • zrem key member – 删除member,O(log N)。
  • zremrangebyrank key start end – 删除排名区间内的member。
  • zremrangebyscore key min max – 删除score区间内的member,支持(开区间。

zincrby

zincrby key increment member – 给member的score增加increment(可为负)。

zinterstore

zinterstore dest keynum key [key...] weights [weight...] [aggregate sum | min | max]

  • dest存交集结果,keynum指定参与求交的zset个数。
  • weights设置权重,与member的score相乘后按aggregate策略合并(sum/min/max)。
  • 最坏时间复杂度O(N*K) + O(M*log M),N为最小集合元素数,K为集合数,M为交集元素数。

zunionstore

用法类似zinterstore,求并集。时间复杂度O(N) + O(M*log M),N是总元素数,M是并集元素数。

zset的应用场景

排行榜系统:微博热搜、游戏天梯、成绩排名。加权排行:维护多个zset,用zinterstore/zunionstore按权重合并。

其他类型

  • stream – 阻塞队列,让Redis当消息队列用。
  • geospatial – 存经纬度坐标,按半径或矩形区域查找。
  • hyperloglog – 估算集合元素个数,最多12KB空间,误差约0.8%,适合对精度要求不高的UV/PV统计。
  • bitmap – 优化过的set,省空间但只支持整数。
  • bitfield – 位域,类似C语言位段,可以对字节中的位做读写计算,目标是省空间。

渐进式遍历

传统keys *一次扫全库容易阻塞,渐进式遍历一次只查一小部分,避免卡住。scan cursor [MATCH match] [COUNT count] [TYPE type]。cursor不是下标,而是服务器返回的下次遍历的开始位置。输入0开始,返回0表示遍历结束。match支持通配符,count默认10。注意:scan不保留状态,中途可以停,但遍历过程中如果数据发生变化,结果可能不准确。

Redis数据库操作

Redis也有“数据库”的概念,不过比MySQL弱。默认有16个库,不能增删,默认操作在0号库。select index切换库(0-15)。dbsize看当前库key数。flushdb [async|sync]清空当前库(异步或同步),flushall清空所有库。

Redis自定义客户端编写

RESP协议

Redis客户端和服务器之间用RESP协议通信,基于TCP,一问一答。客户端用Bulk String数组发送命令,服务器根据命令返回不同格式。

协议格式

+OK\r\n

Simple String,以+开头

-ERR\r\n

Error,以-开头

:1000\r\n

整数,以:开头

$5\r\nhello\r\n

Bulk String,以$开头,可传二进制数据

*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n

Array,以*开头

Redis-plus-plus

声明连接对象:

#include 
sw::redis::Redis redis("tcp://127.0.0.1:6379");

然后可以通过ping检查连通性。

String

set、get方法:

sw::redis::Redis redis("tcp://127.0.0.1:6379");
using namespace std::chrono_literals;
redis.set("key1", "val1", 0s, sw::redis::UpdateType::NO_EXIST);
auto ret = redis.get("key1");
if(ret) std::cout << ret.value() << std::endl;

set方法用sw::redis::StringView作为key和value,高效只读。返回值是sw::redis::OptionalString,可隐式转bool判断是否存在,有效时用value()取出。第三个参数是过期时间(std::chrono::milliseconds),第四是更新策略:EXIST对应setxx,NOT_EXIST对应setnx,ALWAYS就是普通set。

exists和del:

sw::redis::Redis redis("tcp://127.0.0.1:6379");
auto ret = redis.exists("key1");
ret = redis.del({"key1", "key2"});

keys:

sw::redis::Redis redis("tcp://127.0.0.1:6379");
vector ret; 
auto it = std::back_inserter(ret);
redis.keys("*", it);

expire和ttl:

sw::redis::Redis redis("tcp://127.0.0.1:6379");
using namespace std::chrono_literals;
bool ret = redis.expire("key", 10s);  
std::this_thread::sleep_for(5s);
long long len = redis.ttl("key");

type:

sw::redis::Redis redis("tcp://127.0.0.1:6379");
redis.set("key", "val");
std::string ret = redis.type("key");

mget/mset:

sw::redis::Redis redis("tcp://127.0.0.1:6379");
redis.mset({std::make_pair("key1", "val1"), std::make_pair("key2", "val2")});
std::vector keys;
redis.mset(keys.begin(), keys.end());
std::vector vals; 
auto bit = std::back_inserter(vals);
redis.mget({"key1", "key2"}, bit);
std::vector vals2 = {"key1", "key2"};
redis.mget(vals2.begin(), vals2.end(), bit);

getrange/setrange:

sw::redis::Redis redis("tcp://127.0.0.1:6379");
std::string ret = redis.getrange("key", 5, 10);
long long val = redis.setrange("key", 5, "ins_val");

incr/decr:

sw::redis::Redis redis("tcp://127.0.0.1:6379");
long long ret1 = redis.incr("key");
long long ret2 = redis.decr("key");

list

lpush/lrange/rpush/rpop/lpop:

sw::redis::Redis server("tcp://127.0.0.1:6379");
server.lpush("list1", "1");
server.lpush("list1", {"1", "2", "3"});
std::vector vals = "1", "2", "3";
server.lpush("list1", vals.begin(), vals.end());
std::vector rets;
auto bit = std::back_inserter(rets);
server.lrange("list1", bit);
auto ret1 = server.rpop("list1"); 
auto ret2 = server.lpop();

brpop/blpop:

sw::redis::Redis server("tcp://127.0.0.1:6379");
auto ret = server.blpop("list1", std::chrono::seconds(1));
if(ret){
    std::cout << ret.value().first << ret.value().second << std::endl;
}

llen:

sw::redis::Redis server("tcp://127.0.0.1:6379");
long long server.llen("list");

Set

sadd/smembers:

sw::redis::Redis server("tcp://127.0.0.1:6379");
server.sadd("set", "1");
set mems; 
auto in = std::inserter(mems, mems.end());
server.smembers("set", in);

sismember/spop/scard:

sw::redis::Redis server("tcp://127.0.0.1:6379");
bool exists = server.ismember("set", "1");
auto ret = spop("set"); 
if(ret) std::cout << ret.value() << std::endl;
long long num = scard("set");

Hash

hget/hset:

sw::redis::Redis server("tcp://127.0.0.1:6379");
long long ret = server.hset("hash1", "1", "zhangsan");
ret = server.hset("hash1", {std::make_pair("2", "lisi"), std::make_pair("3", "wangwu")});
auto val = server.hget("hash1", "1"); 
if(val) std::cout << val.value() << std::endl;

hexists/hdel/hlen:

sw::redis::Redis server("tcp://127.0.0.1:6379");
bool exists = server.hexists("hash1", "1");
long long ret = server.hdel("hash1", {"1", "2"});
ret = server.hlen("hash1");

hmset/hmget/hkeys/hvals:

std::vector keys;    
auto it = std::back_inserter(keys);
server.hkeys("hash1", it);
std::vector vals;
it = std::back_inserter(vals);
server.hvals("hash1", it);
server.hmget("hash1", {"3", "4", "5"}, it);

ZSet

zadd/zrange:

server.zadd("zset1", "zhangsan", 1);
server.zadd("zset1", {std::make_pair("lisi", 2), std::make_pair("wangwu", 3)});
std::vector> pairs = {
    std::make_pair("zhouliu", "4"),
    std::make_pair("tianqi", "5")
};
server.zadd("zset1", pairs.begin(), pairs.end());
std::vector withnoscore;
auto it = std::back_inserter(withnoscore);
std::vector> withscore;
auto it2 = std::back_inserter(withscore);
server.zrange("zset1", 0, -1, it2);

zcard/zscore/zrank/zrem:

long long ret = server.zcard("zset1");
auto score = server.zscore("zset1", "tianqi");
auto rank = server.zrank("zset1", "zhangsan");
server.zrem("zset1", "lisi");

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

热游推荐

更多
湘ICP备2026025700号-3 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。