暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

redis初识

210

redis是一个常用的开源的内存数据库,常被用作缓存,读写性能极快。支持数据持久化,可以将内存中的数据保存在磁盘中。支持如下数据类型:字符串,哈希,列表,集合,有序集合。

redis.svg

编译安装

获取源码,编译,安装到指定目录:

git clone https://github.com/redis/redis.git cd redis make CFLAGS="-O0 -g" MALLOC=jemalloc make install PREFIX=/home/postgres/redis # 安装到指定目录

配置redis.conf,还有非常多的配置项,这里只列出最常用的几个:

port 6379 #监听端口 dir /home/postgres/redis/data #数据目录 logfile /home/postgres/redis/redis.log #日志文件 pidfile /home/postgres/redis/redis.pid #pid文件 maxmemory 2gb #最大内存 timeout 300 #客户端空闲断开连接超时时间,单位秒 loglevel debug #日志级别 databases 16 #数据库数量

启动redis,使用默认配置:

postgres@slpc:~/redis$ ./bin/redis-server 60088:C 06 May 2025 17:09:50.146 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 60088:C 06 May 2025 17:09:50.146 * Redis version=8.0.0, bits=64, commit=e91a340e, modified=0, pid=60088, just started 60088:C 06 May 2025 17:09:50.146 # Warning: no config file specified, using the default config. In order to specify a config file use ./bin/redis-server /path/to/redis.conf 60088:M 06 May 2025 17:09:50.146 * monotonic clock: POSIX clock_gettime _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis Open Source .-`` .-```. ```\/ _.,_ ''-._ 8.0.0 (e91a340e/0) 64 bit ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 60088 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | https://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 60088:M 06 May 2025 17:09:50.148 * Server initialized 60088:M 06 May 2025 17:09:50.148 * Ready to accept connections tcp

启动redis,使用自定义配置:

postgres@slpc:~/redis$ redis-server redis.conf # 启动redis,使用自定义配置 postgres@slpc:~/redis$ ls bin data redis.conf redis.log redis.pid # redis启动后,生成了redis.log以及redis.pid文件,数据文件在data目录下 postgres@slpc:~/redis$ tail -f redis.log 60303:C 06 May 2025 17:13:50.450 * Configuration loaded 60303:M 06 May 2025 17:13:50.451 * monotonic clock: POSIX clock_gettime 60303:M 06 May 2025 17:13:50.452 * Running mode=standalone, port=6379. 60303:M 06 May 2025 17:13:50.452 * Server initialized 60303:M 06 May 2025 17:13:50.453 . The AOF directory appendonlydir doesn't exist 60303:M 06 May 2025 17:13:50.453 * Ready to accept connections tcp

通过客户端连接redis,验证

postgres@slpc:~/redis/bin$ redis-cli 127.0.0.1:6379> ping # 测试连接 PONG 127.0.0.1:6379> set mykey hangzhou OK 127.0.0.1:6379> get mykey "hangzhou"

redis的基本使用

字符串类型是最基本的类型,一个key对应一个value。

# 字符串类型,一个键最大能存储512MB 127.0.0.1:6379> set tianjin "shentongdb" OK 127.0.0.1:6379> get tianjin "shentongdb"

哈希是一个键值对集合

127.0.0.1:6379> hmset db beijing kingbasedb tianjing shentongdb wuhan damengdb OK 127.0.0.1:6379> hgetall db #获取在哈希表中指定 key 的所有字段和值 1) "beijing" 2) "kingbasedb" 3) "tianjing" 4) "shentongdb" 5) "wuhan" 6) "damengdb" 127.0.0.1:6379> hmget db beijing # 获取所有给定字段的值 1) "kingbasedb"

列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

127.0.0.1:6379> lpush database dameng #将一个或多个值插入到列表头部 (integer) 1 127.0.0.1:6379> lpush database kingbase (integer) 2 127.0.0.1:6379> lrange database 0 10 1) "kingbase" 2) "dameng"

集合是String类型的无序集合,不允许重复成员。集合是通过哈希表实现的,所以添加、删除、查找的复杂度都是O(1)。

127.0.0.1:6379> sadd city tianjing #将一个元素添加到集合 (integer) 1 127.0.0.1:6379> sadd city beijing (integer) 1 127.0.0.1:6379> sadd city hangzhou (integer) 1 127.0.0.1:6379> smembers city #返回集合中的所有成员 1) "tianjing" 2) "beijing" 3) "hangzhou"

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。有序集合的成员是唯一的,但分数(score)却可以重复。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

127.0.0.1:6379> zadd mycity 1 nanjing # 向有序集合添加一个或多个成员,或者更新已存在成员的分数 (integer) 1 127.0.0.1:6379> zadd mycity 2 guilin (integer) 1 127.0.0.1:6379> zadd mycity 5 hangzhou (integer) 1 127.0.0.1:6379> zadd mycity 3 qingdao (integer) 1 127.0.0.1:6379> zadd mycity 4 tianjin (integer) 1 127.0.0.1:6379> zrange mycity 0 10 withscores #在有序集合中计算指定字典区间内成员数量 1) "nanjing" 2) "1" 3) "guilin" 4) "2" 5) "qingdao" 6) "3" 7) "tianjin" 8) "4" 9) "hangzhou" 10) "5"

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

127.0.0.1:6379> pfadd base "beijing" "tianjin" "qingdao" "xian" "nanjing" (integer) 1 127.0.0.1:6379> pfcount base (integer) 5

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。

127.0.0.1:6379> subscribe mychannel # 订阅消息 1) "subscribe" 2) "mychannel" 3) (integer) 1 1) "message" # 接收到消息 2) "mychannel" 3) "oscar" postgres@slpc:~/works/opensource/redis$ redis-cli # 发布消息 127.0.0.1:6379> publish mychannel "oscar" (integer) 1

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set db postgres QUEUED 127.0.0.1:6379(TX)> get db QUEUED 127.0.0.1:6379(TX)> exec 1) OK 2) "postgres"

主流程

redis主流程如下,启动后会做一些初始化工作,然后进入事件循环,等待命令请求。

main(int argc, char **argv) --> spt_init(argc, argv); // 初始化进程标题,通过ps命令看到的进程名称 --> zmalloc_set_oom_handler(redisOutOfMemoryHandler); // 设置内存分配失败时的自定义处理函数 --> initServerConfig(); // 设置 Redis 的全局默认配置参数 --> ACLInit(); // ACL(访问控制列表)子系统的初始化 --> moduleInitModulesSystem(); // 初始化模块系统 --> connTypeInitialize(); // 初始化连接类型 --> initServer(); // 服务端初始化 --> ThreadsManager_init(); --> createSharedObjects(); --> adjustOpenFilesLimit(); --> aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR); // 事件循环初始化 --> aeApiCreate(eventLoop) --> epoll_create(1024); // 创建 epoll 实例 --> evictionPoolAlloc(); --> slowlogInit(); --> applyWatchdogPeriod(); --> ACLLoadUsersAtStartup(); --> initListeners(); // 初始化网络监听,注册监听事件 --> connListen(listener) // 创建监听套接字 --> createSocketAcceptHandler(listener, connAcceptHandler(listener->ct)) --> aeCreateFileEvent(server.el, sfd->fd[j], AE_READABLE, accept_handler,sfd) --> aeApiAddEvent(eventLoop, fd, mask) --> epoll_ctl(state->epfd,op,fd,&ee) --> InitServerLast(); --> redisSetCpuAffinity(server.server_cpulist); --> setOOMScoreAdj(-1); --> aeMain(server.el); while (!eventLoop->stop) aeProcessEvents(eventLoop, AE_ALL_EVENTS | AE_CALL_BEFORE_SLEEP | AE_CALL_AFTER_SLEEP); --> aeDeleteEventLoop(server.el);

redis服务端会调用aeApiPoll函数等待事件,当事件发生时,会调用aeApiPoll函数,该函数会调用epoll_wait函数,该函数会阻塞当前线程,直到有事件发生。调用栈如下:

aeApiPoll(aeEventLoop * eventLoop, struct timeval * tvp) (redis\src\ae_epoll.c:93) aeProcessEvents(int flags, aeEventLoop * eventLoop) (redis\src\ae.c:398) aeMain(aeEventLoop * eventLoop) (redis\src\ae.c:495) main(int argc, char ** argv) (redis\src\server.c:7553)

redis与postgres对比

redis与postgres相比,两者的应用场景和技术实现都有非常大的差异。

在应用场景上,redis核心场景为高速缓存(比如缓存数据库查询结果),而postgres核心场景为事务型应用,OLTP场景,存储结构化数据。虽然redis也可以持久化存储数据,但是其优势场景依然是作为缓存使用。为什么redis非常快呢?因为它简单,没有复杂的查询和事务处理,没有WAL日志,没有MVCC,无需解析器,不用生成执行计划,最重要的一点,他的全量数据是存放在内存中的,不用像postgres,buffer中没有还需要访问磁盘,这当然快了,相比postgres,其精简了大多数的处理过程,使得redis的读写速度非常快。

在功能上,redis与postgres支持的数据类型有较大区别,redis支持string、hash、list、set、zset、bitmap、hyperloglog、geo等数据类型,而postgres支持的数据类型更多,除了integer,text, timestamp等,还支持数组、JSON、JSONB、XML、UUID、HSTORE、CLOB、BLOB等。

在持久化上,redis以内存为主,可进行持久化配置(AOF会削弱redis缓存性能),而postgres以磁盘为主,支持ACID事务。

在事务支持上,postgres支持完整的ACID事务,支持隔离级别,而redis支持简单事务,无法回滚,无隔离级别。

在资源消耗上,redis对内存要求较高,全量数据在内存中,内存必须足够,而postgres对内存要求较低,内存作为缓存使用,磁盘作为持久化存储,内存不足时会将数据淘汰到磁盘,从而降低内存压力。

典型应用场景:
用户请求 -> Redis 查询缓存 -> 未命中 -> 查询 PostgreSQL -> 结果写入 Redis

最后修改时间:2025-05-22 11:04:43
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论