Redis笔记
Redis 是一个高性能的、键值对数据库,数据直接存储在内存中,只有需要持久化时才写入硬盘。
安装启动
ubuntu 下使用 apt 安装的 redis 版本较老,可采用源码安装。
1 | wget https://download.redis.io/releases/redis-6.2.6.tar.gz && tar xzf redis-6.2.6.tar.gz |
安装完成后,会有以下命令可用:
- redis-cli: Redis客户端
- redis-server: Redis服务器启动命令
- redis-benchmark: 性能测试工具
- redis-check-aof: 修复有问题的AOF文件
- redis-check-rdb: 修复有问题的RDB文件
- redis-sentinel: Redis集群使用
设置好配置文件后,启动方法为:
1 | redis-server /etc/redis/redis.conf |
配置文件
Redis 启动时有很多配置参数,这些参数设置均在 redis.conf 中,且有注释和分类,注意:redis 配置文件中注释必须以 # 开头,且必须单独一行。
除了在启动前通过修改配置文件的方式修改配置,启动后也可通过CONFIG SET/GET命令修改、查看配置。
Redis 的所有特性和功能都在配置文件中有所体现,建议仔细了解。
NETWORK:
1 | bind 127.0.0.1 ::1 # 允许指定地址的host访问server,如果要全部都能访问,注释掉该行 |
GENERAL:
1 | daemonize yes # 设置守护进程(后台启动),关闭当前终端后不会关闭redis |
SNAPSHOTTING 保存数据到磁盘文件 RDB
1 | save 3600 1 300 100 60 10000 |
REPLICATION 同步到从机
1 | replicaof <masterip> <masterport> |
SECURITY:
1 | requirepass 123456 # 设置server密码,默认不带密码!!! |
CLIENTS:
1 | maxclients 120 # 最多连接的客户端数目,设置同一时间最大客户端连接数,默认无限制, |
MEMORY:
1 | maxmemory <bytes> # 指定 Redis 最大内存限制,Redis 数据都在内存中,达到最大内存后, |
APPEND ONLY MODE 保存写操作到文件 AOF
1 | appendonly no # 是否启用AOF,将写操作记录到文件,以便重启时恢复数据 |
Docker 运行
Redis 官方有提供镜像,可直接docker pull redis:6.2.6拉取。
主要是启动时的端口和文件映射,需要根据Redis存储的文件路径和使用的端口确定。下面是一个例子:
1 | # 1. 准备一个空文件夹存放redis相关的数据 |
注意事项:
配置文件的中设置不对会造成容器启动后又马上
Exit,一个简单的示例如下:1
2
3
4
5
6
7
8
9# bind 127.0.0.1 ::1
# daemonize 必须设置为默认的 no
daemonize no
# 设置RDB备份文件目录
dir /data/
# 目录不存在也会导致容器自动停止
logfile "/data/redis-server.log"
# 设置密码
requirepass 123456配置文件中的相关路径要有效。
如果在
Docker中运行Redis,并且设置daemonize为yes,即将Redis进程守护化时,最终的Docker exec进程(启动Redis的那个进程)就无事可做了,所以该进程退出,容器也随之结束。来自 stackoverflow 上的回答如果希望
redis后台运行可以使用-d选项,不过这样启动失败时难以查看错误的日志信息,所以可以先不带该选项测试是否可正常启动。无误后再使用-d选项。启动失败后,记得删除掉该容器,再重新尝试启动,否则名字冲突导致启动失败。
也可以自制镜像,将设置好的配置文件拷贝到容器中。
1 | FROM redis |
基础操作
redis-cli
1 | redis-cli -h 127.0.0.1 -p 6379 -a password |
| cmd | explain |
|---|---|
| auth passwd | 授权 |
| ping | 测试连通性 |
| shutdown | 关闭数据库服务进程 redis-server |
| select index | 切换数据库 |
| create index | 创建数据库 |
| drop index | 删除数据库 |
Key
| cmd | explain |
|---|---|
| keys * | 查看当前库的所有key |
| exists key | 查看key是否存在 |
| del key | 删除key |
| unlink key | 删除key,只是将key从数据库中删除,真正的删除会在后续异步操作 |
| type key | 查看key的类型 |
| expire key seconds | 设置key的过期时间 |
| ttl key | 查看key的过期时间 |
| dbsize | 查看当前库的key数量 |
| flushdb | 清空当前库 |
| flushall | 清空所有库 |
虽然 Redis 是 key-value 的方式存储数据,key 与 key 之间没有关系,但通常可以通过 key 的命名来提供一定的“信息”,Redis 的官方文档推荐使用冒号:作为键名的分隔符,因为它可以提高可读性和维护性,并且可以利用SCAN命令进行模式匹配。并且通常在使用 GUI 工具查看数据时,也会用:来分组。
1 | set md|dv:label:json 0 |
上面的几个 key 在 redis-desktop-manager 中显示效果如下:

注意:是以 : 分割,key中的|没有实际意义。
String
String 是二进制安全的,可以包含任何数据。如jpg图片或者序列化的对象,但最大长度512MB。
String 的数据结构为动态字符串,当字符串小于1Mb时,加倍扩容;大于1Mb时,一次扩容增加1Mb。
| cmd | explain |
|---|---|
| set key value | 设置key的值,如果key存在,则覆盖 |
| get key | 查看 key 对应的值 |
| append key value | 在key的值后面追加value |
| strlen key | 查看key的长度 |
| setnx key value | 只有当key不存在时,才设置key的值,否则不设置 |
| incr key | 将key的值增加1 |
| decr key | 将key的值减少1 |
| incrby key increment | 将key的值增加increment |
| decrby key decrement | 将key的值减少decrement |
批量操作的命令:
| cmd | explain |
|---|---|
| mset key1 value1 … keyN valueN | 设置多个key的值 |
| mget key1 … keyN | 获取多个key的值 |
| msetnx key1 value1 … keyN valueN | 只有当所有key都不存在时,才设置多个key的值,一个失败则全部失败 |
| getrange key start end | 获取key的值的一部分 [start, end], 包含start 和end |
| setrange key offset value | 设置key的值的一部分 [offset, offset+value.length()], 如果offset+value.length()大于key的长度,则扩展key的长度 |
| setex key seconds value | 设置key的值并设置过期时间 |
| getset key value | 设置key的值,并返回key的旧值 |
List
列表是值的结构,多个字符串的集合,底层是一个双向链表,对两端的操作性能很高,对中间的操作性能较差。
底层:quicklist,将部分数据组合为一个ziplist,多个ziplist通过前后指针组合为双向链表(quicklist),避免对单个小值如 int 分配两个前后指针,节约空间。
| cmd | explain |
|---|---|
| lpush key value | 在key的左边添加一个值(也可同时插入多个值) |
| rpush key value | 在key的右边添加一个值 |
| lrange key start stop | 获取key的值的一部分,包含start和stop |
| lpop key | 删除并返回key的左边的值(值在键在,值空键消) |
| rpop key | 删除并返回key的右边的值 |
| lpoplpush source destination | 将source的左边的值移动到destination的右边 |
| lpoprpush source destination | 将source的左边的值移动到destination的右边 |
| rpoprpush source destination | 将source的右边的值移动到destination的左边 |
| rpoplpush source destination | 将source的右边的值移动到destination的左边 |
| lindex key index | 获取key的值的第index个元素 |
| llen key | 获取key的值的长度 |
| linsert key before | after pivot value |
| lrem key count value | 从左边删除count个值为value的元素 |
| lset key index value | 设置key的值的第index个元素的值 |
Hash
值部分也是 field-value 结构!
底层有两种:当数据较少时用zipist,当数据较多时用hashtable。
| cmd | explain |
|---|---|
| hset key field value | 设置key的值的field的值 |
| hget key field | 获取key的值的field的值 |
| hmset key field1 value1 … fieldN valueN | 设置key的值的多个field的值 |
| hexists key field | 判断key的值是否包含field |
| hkeys key | 获取key的值的所有field |
| hvals key | 获取key的值的所有value |
| hlen key | 获取key的值的长度 |
| hdel key field | 删除key的值的field |
| hincrby key field increment | 设置key的值的field的值加increment |
| hsetnx key field value | 只有在key的值的field不存在时,才设置key的值的field的值 |
Set
string 类型的无序集合,底层为哈希表,与list类似,但是没有重复的值,每个值都是唯一的。
底层:字典,用hash实现。
| cmd | explain |
|---|---|
| sadd key value | 将一个或多个值插入到key的集合中 |
| smembers key | 获取key的值的所有元素 |
| sismember key value | 判断key的值是否包含value |
| scard key | 获取key的值的长度 |
| srem key value | 删除key的值中的value |
| spop key | 随机获取并删除key的值中的一个元素 |
| srandmember key | 随机获取key的值中的一个元素,但不删除 |
| smove source destination value | 将source的值中的value移动到destination的值中 |
| sinter source1 … sourceN | 获取所有source的值的交集 |
| sunion source1 … sourceN | 获取所有source的值的并集 |
| sdiff source1 … sourceN | 获取所有source的值的差集 |
Zset
与set类似,但是一个有序集合,每个值都有一个score,score越小,编号越小
底层:hash + 跳跃表
| cmd | explain |
|---|---|
| zadd key score value | 将一个或多个值插入到key的有序集合中 |
| zrange key start stop [withscores] | 获取key的值的一部分,包含start和stop |
| zrangebyscore key min max [withscores] | 获取key的值的一部分,包含min和max |
| zrevrangebyscore key max min [withscores] | 获取key的值的一部分,包含max和min |
| zincrby key increment value | 设置key的值的field的值加increment |
| zrem key value | 删除key指定值的field |
| zcount key min max | 获取指定区间的元素个数 |
| zrank key value | 获取key的值的field的编号,从0开始 |
Bitmap
bitmaps 本身不是一种数据类型,就是字符串,但可以对位进行操作。可以把bitmaps想象成一个以位为单位的数组,每一位只能存储0或1,数组的下标在bitmaps中叫做偏移量offset。
| cmd | explain |
|---|---|
| setbit key offset value | value 只能是 0 或 1 |
| getbit key offset | 获取偏移位上的值 |
| bitcount key [start end] | 获取指定区间(未指定区间则全部)的1元素个数,区间单位是字节byte 而不是 bit |
| bitop operation destkey srckey1 … srckeyN | 进行指定操作,操作类型有:and, or, xor, not |
Geo
就是元素的二维坐标,在地图上就是经纬度。redis提供了经纬度设置、查询、范围查询、距离查询、经纬度Hash等功能。
有效的经纬度范围是:-180<=经度<=180, -85<=纬度<=85,坐标位置超过范围时,返回错误。
| cmd | explain |
|---|---|
| geoadd key longitude latitude member [longitude latitude member …] | 将一个或多个值插入到key的Geo中 |
| geodist key member1 member2 | 获取两个元素之间的距离 |
| geopos key member1 member2 | 获取两个元素的坐标 |
| geodist key member1 member2 [m/km/ft/mi] | 获取两个元素之间的距离 |
| georadius key longitude latitude radius [m/km/ft/mi] | 获取指定范围内的元素 |
事物
Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis 事务的主要作用就是串联多个命令防止别的命令插队。
1 | 127.0.0.1:6379> multi # 开始记录 |
从输入 Multi 命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入 Exec 后,Redis 会将之前的命令队列中的命令依次执行。组队的过程中可以通过 discard 来放弃组队。
错误
错误可能发生在组队阶段,或者执行阶段。
- 组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。
- 如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
事务三特性
- 单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 - 没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行 - 不保证原子性
事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
hiredis/redis++
异步操作的命令,或使用了回调函数的操作没有给出例子。
redis-cli 建立连接
1 |
|
Key ops
| cmd | redis++ |
|---|---|
| keys * | std::vector<std::string> keys = redis.keys("*"); |
| exists key | bool exists = redis.exists("key"); |
| del key | int deleted = redis.del("key"); |
| unlink key | int unlinked = redis.unlink("key"); |
| type key | sw::redis::RedisType type = redis.type("key"); |
| expire key seconds | bool success = redis.expire("key", 60); |
| ttl key | long long ttl = redis.ttl("key"); |
| dbsize | long long size = redis.dbsize(); |
| flushdb | bool success = redis.flushdb(); |
| flushall | bool success = redis.flushall(); |
String ops
| cmd | redis++ |
|---|---|
| set key value | bool success = redis.set("key", "value"); |
| get key | std::string value = redis.get("key"); |
| append key value | int length = redis.append("key", "value"); |
| strlen key | int length = redis.strlen("key"); |
| setnx key value | bool success = redis.setnx("key", "value"); |
| incr key | long long result = redis.incr("key"); |
| decr key | long long result = redis.decr("key"); |
| incrby key increment | long long result = redis.incrby("key", 10); |
| decrby key decrement | long long result = redis.decrby("key", 10); |
批量操作的命令:
| cmd | explain |
|---|---|
| mset key1 value1 … keyN valueN | bool success = redis.mset({"key1", "value1", "key2", "value2"}); |
| mget key1 … keyN | std::vector<std::string> values = redis.mget({"key1", "key2"}); |
| msetnx key1 value1 … keyN valueN | bool success = redis.msetnx({"key1", "value1", "key2", "value2"}); |
| getrange key start end | std::string sub = redis.getrange("key", 1, 3); |
| setrange key offset value | int length = redis.setrange("key", 1, "new"); |
| setex key seconds value | bool success = redis.setex("key", 60, "value"); |
| getset key value | 设置key的值,并返回key的旧值 |
List ops
| cmd | explain |
|---|---|
| lpush key value | int length = redis.lpush("list", "element1", "element2"); |
| rpush key value | int length = redis.rpush("list", "element1", "element2"); |
| lrange key start stop | std::vector<std::string> elements = redis.lrange("list", 0, -1); |
| lpop key | std::string element = redis.lpop("list"); |
| rpop key | std::string element = redis.rpop("list"); |
| lpoplpush source destination | bool success = redis.lpoplpush("source", "destination"); |
| lpoprpush source destination | bool success = redis.lpoprpush("source", "destination"); |
| rpoprpush source destination | bool success = redis.rpoprpush("source", "destination"); |
| rpoplpush source destination | bool success = redis.rpoplpush("source", "destination"); |
| llen key | int64_t len = redis.llen("mylist"); |
| lindex key index | 获取key的值的第index个元素 |
| linsert key before | after pivot value |
| lrem key count value | 从左边删除count个值为value的元素 |
| lset key index value | 设置key的值的第index个元素的值 |
Hash ops
| cmd | explain |
|---|---|
| hset key field value | bool success = redis.hset("hash", "field", "value"); |
| hget key field | std::string value = redis.hget("hash", "field"); |
| hmset key field1 value1 … fieldN valueN | bool success = redis.hmset("hash", {{"field1", "value1"}, {"field2", "value2"}}); |
| hexists key field | bool exists = redis.hexists("hash", "field"); |
| hkeys key | std::vector<std::string> fields = redis.hkeys("hash"); |
| hvals key | std::vector<std::string> values = redis.hvals("hash"); |
| hlen key | int count = redis.hlen("hash"); |
| hdel key field | bool success = redis.hdel("hash", "field"); |
| hincrby key field increment | int value = redis.hincrby("hash", "field", 1); |
| hsetnx key field value | bool success = redis.hsetnx("hash", "field", "value"); |
Set ops
| cmd | explain |
|---|---|
| sadd key value | int count = redis.sadd("set", {"elem1", "elem2", "elem3"}); |
| smembers key | std::vector<std::string> members = redis.smembers("set"); |
| sismember key value | bool exist = redis.sismember("set", "elem"); |
| scard key | int count = redis.scard("set"); |
| srem key value | int count = redis.srem("set", {"elem1", "elem2"}); |
| spop key | std::string elem = redis.spop("set"); |
| srandmember key | std::vector<std::string> elems = redis.srandmember("set", 2); |
| smove source destination value | bool success = redis.smove("set1", "set2", "elem"); |
| sinter source1 … sourceN | std::vector<std::string> elems = redis.sinter({"set1", "set2"}); |






