目录
01 Redis 基础
Pt1 Redis诞生
Pt2 Redis定位
Pt2.1 关系型数据库
Pt2.2 非关系型数据库
Pt2.3 NewSQL
Pt3 Redis特性
02 Redis 安装
Pt1 单机安装Redis
Pt2 Redis客户端工具
03 Redis 存储结构
01 Redis 基础
Pt1 Redis诞生
Redis,是Remote Dictionary Server(远程字典服务)缩写,维基百科对Redis的描述是:
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、分布式、可选持久性的键值对存储数据库。从2015年6月开始,Redis的开发由Redis Labs赞助,而2013年5月至2015年6月期间,其开发由Pivotal赞助。[3]在2013年5月之前,其开发由VMware赞助。根据月度排行网站DB-Engines.com的数据,Redis是最流行的键值对存储数据库。
Redis在分布式互联网应用中非常流行,是什么样的场景催生了Redis的诞生呢?
Redis作者叫antirez,最初他需要做一个记录网站访问情况的系统,比如PV,UV,用户在网站行为等数据。当然现在大部分互联网企业都有类似这种用户画像和用户旅程的数据记录,通常这些记录的数据量是非常惊人的,但是在antirez那个时候,大概2008年,他可能还是第一个。
最初,它使用MySQL来存储,但是性能实在是跟不上,最后迫不得已,antirez写了一个内存的List,这个就是Redis的第一个版本。现在Redis的数据类型和功能已经很丰富了,而且在互联网应用中redis非常的流行,甚至成为了解决性能问题的一大利器。
Pt2 Redis定位
Pt2.1 关系型数据库
Redis我们通常称之为缓存,用来存放一些临时数据,在印象中缓存都是不可靠的,存在数据丢失的情况,但是Redis已经支持数据的持久化,可以作为存储容器来使用了。
我刚上班那会,Redis还不是特别流行,大家研究的都是关系型数据库的存储和性能,但是这些年随着移动互联网的高速发展,还有硬件性能的提升,和数据体量的不断膨胀,关系型数据库很难解决在某些互联网场景下的问题。比如,大数据的分析和计算,实时的数据统计和分析,海量数据的查询等,所以诞生了HBase、Redis、MongoDB、ES等相关的中间件,当然最突出的场景还是性能瓶颈。4G的普及,5G的发展,网络越来越快,硬件性能也越来越好,如果还是使用关系型数据库,点击链接等个几秒钟才有反应, 那显然将会被历史淘汰了,2G已经过去了(还是无比怀念那个时候,那个国产机刷网页看NBA新闻的日子,点一下等个十秒钟,如果有图片就更慢了,但是那时候根本感觉不到慢,能有个手机时时刻刻刷个新闻已经很知足了)。
但是,前面说的这些,只是想说关系型数据库在某些场景下已经不再适用,而不是说关系型数据库完全不行,实际上,关系型数据库的使用在大部分项目中还是最主要的,目前还没有谁可以代替,Redis也只是辅助关系型数据库来解决特定场景下的问题,而不能取代关系型数据库的地位。
关系型数据库有如下特点:
- 1、以表格的形势,基于行存储数据,是二维的模式;
- 2、存储结构化的数据,数据存储有固定的模式(schema),需要适配表结构;
- 3、表与表之间存在关联(Relationship);
- 4、大都支持SQL(结构化查询语言)的操作,支持复杂的关联查询;
- 5、通过支持事务ACID来提供严格或者实时的数据一致性;
- 6、关系型数据库将数据持久化到硬盘存储,保证了数据安全性,不会随着应用的宕机而丢失;
但是,关系型也有一些限制:
- 1、要实现扩容的话,只能向上(垂直)扩展,不支持动态的扩缩容。比如磁盘不够,就要扩大磁盘容量,通过堆硬件的方式,不支持动态的扩缩容。水平扩容就需要通过更加复杂的技术来实现,比如分库分表;
- 2、表结构修改困难,因此存储的数据格式也受到限制;
- 3、关系型数据库通常是将数据持久化到磁盘存储,但是高并发情况下,基于磁盘的读写性能压力比较大;
Pt2.2 非关系型数据库
尤其是第三点性能问题,所以Redis之类的非关系型数据库就诞生了。非关系型数据库,又称为NoSQL,开始要表达的是非结构化查询语言(SQL)数据库的意思,因为大部分关系型数据库都是遵循SQL的操作规范。现在更多地时表示,“Not Only SQL”或者“Non-relational SQL”的意思。
我们看看非关系型数据库的特点:
- 1、存储非结构化的数据,比如文本、图片、音频、视频
- 2、表与表之间没有关联,可扩展性强
- 3、保证数据的最终一致性,遵循BASE理论
- 4、支持海量数据的存储和高并发的高效读写
- 5、支持分布式,能够对数据进行分片存储,扩缩容简单
对于不同的场景,有不同的存储方式和类型,可以将非关系型数据库(后面简称NoSQL)分为几类:
- 1、KV存储:Redis和Memcached
- 2、文档存储:MongoDB
- 3、列存储:HBase
- 4、图存储:Neo4j
- 5、对象存储
- 6、XML存储
- 7、……
种类之多,大部分我竟然都没听过(太惨了),下面这个网站介绍了NoSQL具体实现产品。可以关注下当前有哪些被应用的NoSQL,数量之多令人发指。
总之,NoSQL扩展能力强,对海量数据和高性能场景的支撑较好,但是语法格式、事务还有关系模型的建立不是特别好,这点还是关系型数据库强。
Pt2.3 NewSQL
既然SQL和NoSQL各有优势,是否能把他们的优势结合到一起呢。现在已经有了,SQL和NoSQL结合的新品种,NewSQL。NewSQL结合了SQL和NoSQL的特性,比如TiDBit,VoltDB,ScaleDB。
特性 | 关系模型 | SQL语法 | ACID | 水平扩展 | 海量数据 | 无结构化 |
SQL | √ | √ | √ | × | × | × |
NoSQL | × | × | × | √ | √ | √ |
NewSQL | √ | √ | √ | √ | √ | × |
NewSQL这块我也不是特别了解,当然也不是这块的主题,我们还是重点来看看Redis有什么特性,能够在NoSQL中脱颖而出,成为一个流行的组件。
Pt3 Redis特性
Redis主要有以下几点优势让他成为流行的NoSQL:
- 基于内存的存储,读写速度快,性能高,可以达到10w的QPS;
- 通过数据缓存,减轻数据库压力,提升应用性能;
- 相对于HashMap和Memcached而言,支持更丰富的数据类型,支持多语言;
- 功能丰富:持久化机制,内存淘汰策略,事务,发布订阅,lua等;
- 支持集群部署(分布式);
Redis默认有16个库(编号0-15),这个是在配置文件中redis.conf定义的,可以修改。但是和数据库不同的是,Redis的16个库没有完全隔离,所以并不适合把不同的库分配给不同的业务使用,默认就是使用第一个db0。
# redis.conf配置数据库个数
database 16
# 切换数据库
select 1
# 清空当前数据库
flushdb
# 清空所有数据库
flushall
Redis的存储称为KV(key-value)存储,或者叫字典结构,key的最大长度限制是512M,value的限制则不同,有的是长度限制,有的是个数限制,下面介绍数据类型我们会逐个介绍。
Redis和Memcached有什么区别呢?
- redis和memcached都是内存型数据库,二者都支持KV型数据存储,但是redis还支持list、set和hash等方式的存储,这些Memcached不支持。虽然Memcached支持存储图片、视频等,但是对于大多数场景来说,这些比较鸡肋,用的比较少。
- Redis支持数据持久化,可以通过RDB和AOF方式将数据持久化到磁盘,保证数据安全性。Memcached则不行,不支持数据持久化。
- Redis支持原生的集群模式。在 redis3.x 版本中,便能支持 cluster 模式,而 memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。
02 Redis 安装
Pt1 单机安装Redis
正好我刚买了一个云主机,直接在上面安装。
环境:CentOS 7.5 64位
容器:Docker
版本:Redis Lastest
安装步骤如下。
(1)获取Redis的Docker镜像
# 搜索redis的Docker镜像
[root@VM-0-17-centos ~]# docker search redis
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
redis Redis is an open source key-value store that… 8890 [OK]
bitnami/redis Bitnami Redis Docker Image 169 [OK]
sameersbn/redis 82 [OK]
grokzen/redis-cluster Redis cluster 3.0, 3.2, 4.0, 5.0, 6.0 74
rediscommander/redis-commander Alpine image for redis-commander - Redis man… 48 [OK]
kubeguide/redis-master redis-master with "Hello World!" 33
redislabs/redisearch Redis With the RedisSearch module pre-loaded… 30
redislabs/redis Clustered in-memory database engine compatib… 27
redislabs/rejson RedisJSON - Enhanced JSON data type processi… 23
oliver006/redis_exporter Prometheus Exporter for Redis Metrics. Supp… 22
arm32v7/redis Redis is an open source key-value store that… 22
bitnami/redis-sentinel Bitnami Docker Image for Redis Sentinel 18 [OK]
redislabs/redisinsight RedisInsight - The GUI for Redis 15
redislabs/redisgraph A graph database module for Redis 13 [OK]
webhippie/redis Docker images for Redis 11 [OK]
s7anley/redis-sentinel-docker Redis Sentinel 10 [OK]
arm64v8/redis Redis is an open source key-value store that… 10
insready/redis-stat Docker image for the real-time Redis monitor… 9 [OK]
redislabs/redismod An automated build of redismod - latest Redi… 8 [OK]
circleci/redis CircleCI images for Redis 5 [OK]
centos/redis-32-centos7 Redis in-memory data structure store, used a… 5
clearlinux/redis Redis key-value data structure server with t… 3
tiredofit/redis Redis Server w/ Zabbix monitoring and S6 Ove… 1 [OK]
wodby/redis Redis container image with orchestration 1 [OK]
xetamus/redis-resource forked redis-resource 0 [OK]
# 获取最新镜像
[root@VM-0-17-centos ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
6ec7b7d162b2: Pull complete
1f81a70aa4c8: Pull complete
968aa38ff012: Pull complete
884c313d5b0b: Pull complete
6e858785fea5: Pull complete
78bcc34f027b: Pull complete
Digest: sha256:0f724af268d0d3f5fb1d6b33fc22127ba5cbca2d58523b286ed3122db0dc5381
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
# 查看已下载的镜像
[root@VM-0-17-centos ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest ef47f3b6dc11 9 days ago 104MB
(2)配置Redis
因为Docker安装的Redis默认没有配置文件,所以需要挂载主机的配置文件。
# 在主机环境中创建映射的配置和数据目录
mkdir -p /root/app/redis/conf/
mkdir -p /root/app/redis/data/
复制 redis.conf
文件到主机/root/app/redis/conf/目录下(这个配置文件可以在官网redis.io下载的压缩包中找到)。
# 注意有两行配置要修改:
daemonize yes # 这一行必须注释,否则无法启动。该选项让redis成为在后台运行的守护进程,而docker容器必须要有一个前台进程才能留存。
bind 127.0.0.1 # 改成 bind 0.0.0.0 ,否则只能在本机访问
(3)启动Redis服务端
运行Redis服务端:
[root@VM-0-17-centos ~]# docker run -p 6379:6379 --name single-redis -v /root/app/redis/conf/redis.conf:/etc/redis/redis.conf -v /root/app/redis/data/:/data -d redis:latest redis-server /etc/redis/redis.conf --appendonly yes
91574e89d068f0da6d2cf3e98732cc2d6b5699ebe222af2e24ba6a9a73e38ab1
参数说明:
- -d 后台运行
- -p 6379:6379 端口映射(本机6379端口映射容器6379端口)
- –name single-redis 容器别名
- -v /root/app/redis/conf:/conf 目录映射(本机redis配置文件目录)
- -v /root/app/redis/data:/data 目录映射(本机redis数据目录)
- redis-server /conf/redis.conf --appendonly yes 在容器运行命令,并打开数据持久化
(4)连接Redis客户端
连接Redis客户端:
docker exec -it single-redis redis-cli
Pt2 Redis客户端工具
下载软件Redis Desktop Manager,安装后就是一个可视化的Redis查看工具。
这个是收费软件,商用的话要注意版权问题,具体安装比较简单,exe文件。
03 Redis 存储结构
Redis存储结构主要包含以下几个关键实现:
- redisDb:见名知意,redis的数据库,redisDb中包含了dict对象;
- dict:字典,字典里存放的就是哈希表dictht;
- dictht:Redis是利用哈希表dictht存储数据的,哈希表包含了数组,数组里存放了指向dictEntry的指针。
- dictEntry:dictEntry是存放键值对的数据结构,并且包含了指向下一个节点的指针,用于在哈希冲突时使用拉链发来解决,和hashmap的思路是一致的。
具体源码实现如下(源码可以在官网下载):
typedef struct redisDb {
dict *dict; /* 所有键值对 *//* The keyspace for this DB */
dict *expires; /* 所有设置了过期时间的键值对 *//* Timeout of keys with a timeout set */
dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/
dict *ready_keys; /* Blocked keys that received a PUSH */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
long long avg_ttl; /* Average TTL, just for stats */
unsigned long expires_cursor; /* Cursor of the active expire cycle. */
list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;
typedef struct dict {
dictType *type; /* type中定义了对于Hash表的操作函数,比如Hash函数,key比较函数等 */
void *privdata; /* 私有数据 */
dictht ht[2]; /* 两张哈希表,一个用于存储数据,另一个用于扩容是数据拷贝 */
long rehashidx; /* rehash索引 */
unsigned long iterators; /* 当前正在使用的迭代器数据量 */
} dict;
typedef struct dictht {
dictEntry **table; /* table是一个数组,里面存放的是指针,指向的是dictEntry 这种结构。 */
unsigned long size; /* 哈希表的大小 */
unsigned long sizemask; /* 掩码大小,用于计算索引值,总是等于size-1 */
unsigned long used; /* 已包含节点数 */
} dictht;
typedef struct dictEntry {
void *key; /* KV结构中key的指针 */
union {
void *val; /* KV结构中value的指针 */
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next; /* 指向下一个节点的指针,是采用拉链法解决哈希冲突 */
} dictEntry;
这里先简单介绍数据结构,后面针对每种数据类型,会做更加详细的分析。