履霜坚冰至
1. NoSQL概述
- 定义
NoSQL,泛指非关系型的数据库,Not Only Structured Query Language
- 使用原因
- 单机MySQL时代
- Memcached(缓存) + Mysql + 垂直拆分(读写分离)
- 分库分表 + 水平拆分 + Mysql集群
- NoSQL+分库分表 + 水平拆分 + Mysql集群
用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长, 这时候我们就需要使用NoSQL数据库的
- 分类
分类 | Examples举例 | 典型应用场景 | 数据模型 | 优点 | 缺点 |
---|---|---|---|---|---|
键值(key-value) | Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB | 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 | Key 指向 Value 的键值对,通常用hash table来实现 | 查找速度快 | 数据无结构化,通常只被当作字符串或者二进制数据 |
列存储数据库 | Cassandra, HBase, Riak | 分布式的文件系统 | 以列簇式存储,将同一列数据存在一起 | 查找速度快,可扩展性强,更容易进行分布式扩展 | 功能相对局限 |
文档型数据库 | CouchDB, MongoDb | Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) | Key-Value对应的键值对,Value为结构化数据 | 数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构 | 查询性能不高,而且缺乏统一的查询语法。 |
图形(Graph)数据库 | Neo4J, InfoGrid, Infinite Graph | 社交网络,推荐系统等。专注于构建关系图谱 | 图结构 | 利用图结构相关算法。比如最短路径寻址,N度关系查找等 | 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。 |
- 特点
- 易扩展
- 大数据量,高性能(Redis 一秒读取11万,写8万)
- 灵活的数据模型
- 高可用
- 最终一致性CAP定理和BASE理论
- 大数据时代3v:主要是描述问题的
- 海量Velume
- 多样Variety
- 实时Velocity
- 大数据时代的3高 : 主要是对程序的要求
- 高并发
- 高可扩
- 高性能
2. Redis概述
- 定义
Redis(Remote Dictionary Server ),即远程字典服务。是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
- 下载安装
- windows安装
建议在github下载 [redis下载](https://github.com/dmajkic/redis)
- Linux安装
1 | # 解压 |
3. Redis性能测试工具
- 语句
1 | redis-benchmark [option] [option value] |
序号 | 选项 | 描述 | 默认值 |
---|---|---|---|
1 | -h | 指定服务器主机名 | 127.0.0.1 |
2 | -p | 指定服务器端口 | 6379 |
3 | -s | 指定服务器 socket | |
4 | -c | 指定并发连接数 | 50 |
5 | -n | 指定请求数 | 10000 |
6 | -d | 以字节的形式指定 SET/GET 值的数据大小 | 2 |
7 | -k | 1=keep alive 0=reconnect | 1 |
8 | -r | SET/GET/INCR 使用随机 key, SADD 使用随机值 | |
9 | -P | 通过管道传输 |
1 |
10 | -q | 强制退出 redis。仅显示 query/sec 值 | |
11 | –csv | 以 CSV 格式输出 | |
12 | ***-l*(L 的小写字母)** | 生成循环,永久执行测试 | |
13 | ***-l*(L 的小写字母)** | 生成循环,永久执行测试 | |
14 | ***-I*(i 的大写字母)** | Idle 模式。仅打开 N 个 idle 连接并等待。 |
4.Redis基础配置
- 数据库
databases 16,默认16【0-15】个数据库,使用是第0个,可以使用select 3进行切换
1 | 127.0.0.1:6379> flushdb # 清空当前数据库 |
- 单线程
- 基于内存和网络带宽,cpu不是性能瓶颈,使用单线程操作效率是最高的
- 基于C语言,每秒查询【QPS】达到10W+
序号 | 参数 | 默认 | 功能 |
---|---|---|---|
1 | daemonize | no | Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程 |
2 | pidfile | /var/run/redis.pid | 后台运行或当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定 |
3 | port | 6379 | 指定Redis监听端口,默认端口为6379,6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字 |
4 | bind | 127.0.0.1 | 绑定的主机地址 |
5 | timeout | 300 | 当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能 |
6 | loglevel | verbose | 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning |
7 | logfile | stdout | 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null |
8 | databases | 16 | 设置数据库的数量,默认数据库为0,可以使用SELECT <dbid> 命令在连接上指定数据库id |
9 | save<seconds> <changes> |
save 900 1 save 300 10 save 60 10000 |
指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合,表示900秒(15分钟)内有1个更改 |
10 | rdbcompression | yes | 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大 |
11 | dbfilename | dump.rdb | 指定本地数据库文件名 |
12 | dir | ./ | 指定本地数据库存放目录 |
13 | slaveof <masterip> <masterport> |
设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步 | |
14 | masterauth <mater-password> |
当master服务设置了密码保护时,slav服务连接master的密码 | |
15 | requirepass | foobared | 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH <password> 命令提供密码,默认关闭 |
16 | maxclients | 128 | 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息 |
17 | maxmemory <bytes> |
指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区 | |
18 | appendonly | no | 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。 |
19 | appendfilename | appendonly.aof | 指定更新日志文件名 |
20 | appendfsync | everysec | 指定更新日志条件,共有3个可选值: no:表示等操作系统进行数据缓存同步到磁盘(快) always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) everysec:表示每秒同步一次(折衷,默认值) |
21 | vm-enabled | no | 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中 |
22 | vm-swap-file | /tmp/redis.swap | 虚拟内存文件路径,不可多个Redis实例共享 |
23 | vm-max-memory | 0 | 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘 |
24 | vm-page-size | 32 | edis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值 |
25 | vm-pages | 134217728 | 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。 |
26 | vm-max-threads | 4 | 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟 |
27 | glueoutputbuf | yes | 设置在向客户端应答时,是否把较小的包合并为一个包发送 |
28 | hash-max-zipmap-entries 64 hash-max-zipmap-value 512 | 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法 | |
29 | activerehashing | yes | 指定是否激活重置哈希 |
30 | include | /path/to/local.conf | 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件 |
31 | protected-model | yes | 保护模式 |
5. 数据类型
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。它支持字符串、哈希表、列表、集合、有序集合,位图,hyperloglogs等数据类型。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。
5.1 基本连接命令
命令 | 描述 |
---|---|
Redis Echo 命令 | 打印字符串 |
Redis Select 命令 | 切换到指定的数据库 |
Redis Ping 命令 | 查看服务是否运行 |
Redis Quit 命令 | 关闭当前连接 |
Redis Auth 命令 | 验证密码是否正确 |
1 | 127.0.0.1:6379> exists name # 判断是否存在key为name |
5.2 Redis-key
命令 | 描述 |
---|---|
Redis Type 命令 | 返回 key 所储存的值的类型。 |
Redis PEXPIREAT 命令 | 设置 key 的过期时间亿以毫秒计。 |
Redis PEXPIREAT 命令 | 设置 key 过期时间的时间戳(unix timestamp) 以毫秒计 |
Redis Rename 命令 | 修改 key 的名称 |
Redis PERSIST 命令 | 移除 key 的过期时间,key 将持久保持。 |
Redis Move 命令 | 将当前数据库的 key 移动到给定的数据库 db 当中。 |
Redis RANDOMKEY 命令 | 从当前数据库中随机返回一个 key 。 |
Redis Dump 命令 | 序列化给定 key ,并返回被序列化的值。 |
Redis TTL 命令 | 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 |
Redis Expire 命令 | seconds 为给定 key 设置过期时间。 |
Redis DEL 命令 | 该命令用于在 key 存在是删除 key。 |
Redis Pttl 命令 | 以毫秒为单位返回 key 的剩余的过期时间。 |
Redis Renamenx 命令 | 仅当 newkey 不存在时,将 key 改名为 newkey 。 |
Redis EXISTS 命令 | 检查给定 key 是否存在。 |
Redis Expireat 命令 | EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置过期时间。 不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 |
Redis Keys 命令 | 查找所有符合给定模式( pattern)的 key 。 |
1 | 127.0.0.1:6379> set name "zz" # 设置key-value或修改值 |
5.3 String
命令 | 描述 |
---|---|
Redis Setnx 命令 | 只有在 key 不存在时设置 key 的值。 |
Redis Getrange 命令 | 返回 key 中字符串值的子字符 |
Redis Mset 命令 | 同时设置一个或多个 key-value 对。 |
Redis Setex 命令 | 将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。 |
Redis SET 命令 | 设置指定 key 的值 |
Redis Get 命令 | 获取指定 key 的值。 |
Redis Getbit 命令 | 对 key 所储存的字符串值,获取指定偏移量上的位(bit)。 |
Redis Setbit 命令 | 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。 |
Redis Decr 命令 | 将 key 中储存的数字值减一。 |
Redis Decrby 命令 | key 所储存的值减去给定的减量值(decrement) 。 |
Redis Strlen 命令 | 返回 key 所储存的字符串值的长度。 |
Redis Msetnx 命令 | 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。 |
Redis Incrby 命令 | 将 key 所储存的值加上给定的增量值(increment) 。 |
Redis Incrbyfloat 命令 | 将 key 所储存的值加上给定的浮点增量值(increment) 。 |
Redis Setrange 命令 | 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。 |
Redis Psetex 命令 | 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。 |
Redis Append 命令 | 如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。 |
Redis Getset 命令 | 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 |
Redis Mget 命令 | 获取所有(一个或多个)给定 key 的值。 |
Redis Incr 命令 | 将 key 中储存的数字值增一。 |
1 | 127.0.0.1:6379> APPEND name1 "ll" # 追加,不存在key则创建 |
5.4 List
实际上是一个链表,before Node after,left,right都可以插入,可以做消息队列(先进先出 lpush rpop),栈(先进后出 lpush lpop),一个列表最多可以包含2^ 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。
命令 | 描述 |
---|---|
Redis Lindex 命令 | 通过索引获取列表中的元素 |
Redis Rpush 命令 | 在列表中添加一个或多个值 |
Redis Lrange 命令 | 获取列表指定范围内的元素 |
Redis Rpoplpush 命令 | 移除列表的最后一个元素,并将该元素添加到另一个列表并返回 |
Redis Blpop 命令 | 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
Redis Brpop 命令 | 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
Redis Brpoplpush 命令 | 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 |
Redis Lrem 命令 | 移除列表元素 |
Redis Llen 命令 | 获取列表长度 |
Redis Ltrim 命令 | 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 |
Redis Lpop 命令 | 移出并获取列表的第一个元素 |
Redis Lpushx 命令 | 将一个或多个值插入到已存在的列表头部 |
Redis Linsert 命令 | 在列表的元素前或者后插入元素 |
Redis Rpop 命令 | 移除并获取列表最后一个元素 |
Redis Lset 命令 | 通过索引设置列表元素的值 |
Redis Lpush 命令 | 将一个或多个值插入到列表头部 |
Redis Rpushx 命令 | 为已存在的列表添加值 |
1 | 127.0.0.1:6379> lpush list 1 2 3 4 # 从左边放入list一个或多个值(和栈相似先进后出) |
5.5 set
不重复的集子,Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。集合中最大的成员数为 2^232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
命令 | 描述 |
---|---|
Redis Sunion 命令 | 返回所有给定集合的并集 |
Redis Scard 命令 | 获取集合的成员数 |
Redis Srandmember 命令 | 返回集合中一个或多个随机数 |
Redis Smembers 命令 | 返回集合中的所有成员 |
Redis Sinter 命令 | 返回给定所有集合的交集 |
Redis Srem 命令 | 移除集合中一个或多个成员 |
Redis Smove 命令 | 将 member 元素从 source 集合移动到 destination 集合 |
Redis Sadd 命令 | 向集合添加一个或多个成员 |
Redis Sismember 命令 | 判断 member 元素是否是集合 key 的成员 |
Redis Sdiffstore 命令 | 返回给定所有集合的差集并存储在 destination 中 |
Redis Sdiff 命令 | 返回给定所有集合的差集 |
Redis Sscan 命令 | 迭代集合中的元素 |
Redis Sinterstore 命令 | 返回给定所有集合的交集并存储在 destination 中 |
Redis Sunionstore 命令 | 所有给定集合的并集存储在 destination 集合中 |
Redis Spop 命令 | 移除并返回集合中的一个随机元素 |
1 | 127.0.0.1:6379> sadd myset 1 2 3 4 1 # 增加myset集子,1重复只会有1个 |
5.6 Hash
Hash是一个string类型的field和value的映射表,Map,相当于key -map,Hash更适合于对象的存储,Sring更加适合字符串
命令 | 描述 |
---|---|
Redis Hmset 命令 | 同时将多个 field-value (域-值)对设置到哈希表 key 中。 |
Redis Hmget 命令 | 获取所有给定字段的值 |
Redis Hset 命令 | 将哈希表 key 中的字段 field 的值设为 value 。 |
Redis Hgetall 命令 | 获取在哈希表中指定 key 的所有字段和值 |
Redis Hget 命令 | 获取存储在哈希表中指定字段的值/td> |
Redis Hexists 命令 | 查看哈希表 key 中,指定的字段是否存在。 |
Redis Hincrby 命令 | 为哈希表 key 中的指定字段的整数值加上增量 increment 。 |
Redis Hlen 命令 | 获取哈希表中字段的数量 |
Redis Hdel 命令 | 删除一个或多个哈希表字段 |
Redis Hvals 命令 | 获取哈希表中所有值 |
Redis Hincrbyfloat 命令 | 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。 |
Redis Hkeys 命令 | 获取所有哈希表中的字段 |
Redis Hsetnx 命令 | 只有在字段 field 不存在时,设置哈希表字段的值。 |
1 | 127.0.0.1:6379> hset myhash f1 v1 # 设置myhash key-value |
5.7 Zset
在set基础上增加了排序,不同的是每个元素都会关联一个double类型的分数score,score相同:按字典顺序排序,有序集合的成员是唯一的,但分数(score)却可以重复。redis正是通过分数来为集合中的成员进行从小到大的排序。
命令 | 描述 |
---|---|
Redis Zrevrank 命令 | 返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 |
Redis Zlexcount 命令 | 在有序集合中计算指定字典区间内成员数量 |
Redis Zunionstore 命令 | 计算给定的一个或多个有序集的并集,并存储在新的 key 中 |
Redis Zremrangebyrank 命令 | 移除有序集合中给定的排名区间的所有成员 |
Redis Zcard 命令 | 获取有序集合的成员数 |
Redis Zrem 命令 | 移除有序集合中的一个或多个成员 |
Redis Zinterstore 命令 | 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 |
Redis Zrank 命令 | 返回有序集合中指定成员的索引 |
Redis Zincrby 命令 | 有序集合中对指定成员的分数加上增量 increment |
Redis Zrangebyscore 命令 | 通过分数返回有序集合指定区间内的成员 |
Redis Zrangebylex 命令 | 通过字典区间返回有序集合的成员 |
Redis Zscore 命令 | 返回有序集中,成员的分数值 |
Redis Zremrangebyscore 命令 | 移除有序集合中给定的分数区间的所有成员 |
Redis Zscan 命令 | 迭代有序集合中的元素(包括元素成员和元素分值) |
Redis Zrevrangebyscore 命令 | 返回有序集中指定分数区间内的成员,分数从高到低排序 |
Redis Zremrangebylex 命令 | 移除有序集合中给定的字典区间的所有成员 |
Redis Zrevrange 命令 | 返回有序集中指定区间内的成员,通过索引,分数从高到底 |
Redis Zrange 命令 | 通过索引区间返回有序集合成指定区间内的成员 |
Redis Zcount 命令 | 计算在有序集合中指定区间分数的成员数 |
Redis Zadd 命令 | 向有序集合添加一个或多个成员,或者更新已存在成员的分数 |
1 | 127.0.0.1:6379> zadd myset 1 one 2 two # 增加两个值,前面的数字代表排序分数 |
5.8 Geospatial(地理位置)
实现原理是zset,可以使用zset命令操作geo,有效的经度从-180度到180度,有效的纬度从-85.05112878度到85.05112878度。当坐标位置超出上述指定范围时,该命令将会返回一个错误。
命令 | 描述 |
---|---|
Redis GEOHASH 命令 | 返回一个或多个位置元素的 Geohash 表示 |
Redis GEOPOS 命令 | 从key里返回所有给定位置元素的位置(经度和纬度) |
Redis GEODIST 命令 | 返回两个给定位置之间的距离 |
Redis GEORADIUS 命令 | 以给定的经纬度为中心, 找出某一半径内的元素 |
Redis GEOADD 命令 | 将指定的地理空间位置(纬度、经度、名称)添加到指定的key中 |
Redis GEORADIUSBYMEMBER 命令 | 找出位于指定范围内的元素,中心点是由给定的位置元素决定 |
1 | 127.0.0.1:6379> GEOADD china:city 16.32 30.33 beijing # 增加城市纬度、经度、名称 |
5.9 Hyperloglog(基数统计)
数据集中不重复的元素的个数,应用场景:网页的访问量(UV),如果允许容错0.81%,那么一定可以使用Hyperloglog
命令 | 描述 |
---|---|
Redis Pfmerge 命令 | 将多个 HyperLogLog 合并为一个 HyperLogLog |
Redis Pfadd 命令 | 添加指定元素到 HyperLogLog 中。 |
Redis Pfcount 命令 | 返回给定 HyperLogLog 的基数估算值。 |
1 | 127.0.0.1:6379> pfadd mykey a b c d e f g #增加 |
5.10 BitMaps(位图)
用位存储,信息状态只有 0 和 1
命令 | 描述 |
---|---|
Redis Setbit 命令 | 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。 |
Redis Getbit 命令 | 对 key 所储存的字符串值,获取指定偏移量上的位(bit)。 |
1 | 127.0.0.1:6379> setbit sign 0 1 # 设置sign的第0位为 1 |
5.11 服务器命令
命令 | 描述 |
---|---|
Redis Client Pause 命令 | 在指定时间内终止运行来自客户端的命令 |
Redis Debug Object 命令 | 获取 key 的调试信息 |
Redis Flushdb 命令 | 删除当前数据库的所有key |
Redis Save 命令 | 异步保存数据到硬盘 |
Redis Showlog 命令 | 管理 redis 的慢日志 |
Redis Lastsave 命令 | 返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示 |
Redis Config Get 命令 | 获取指定配置参数的值 |
Redis Command 命令 | 获取 Redis 命令详情数组 |
Redis Slaveof 命令 | 将当前服务器转变为指定服务器的从属服务器(slave server) |
Redis Debug Segfault 命令 | 让 Redis 服务崩溃 |
Redis Flushall 命令 | 删除所有数据库的所有key |
Redis Dbsize 命令 | 返回当前数据库的 key 的数量 |
Redis Bgrewriteaof 命令 | 异步执行一个 AOF(AppendOnly File) 文件重写操作 |
Redis Cluster Slots 命令 | 获取集群节点的映射数组 |
Redis Config Set 命令 | 修改 redis 配置参数,无需重启 |
Redis Command Info 命令 | 获取指定 Redis 命令描述的数组 |
Redis Shutdown 命令 | 异步保存数据到硬盘,并关闭服务器 |
Redis Sync 命令 | 用于复制功能(replication)的内部命令 |
Redis Client Kill 命令 | 关闭客户端连接 |
Redis Role 命令 | 返回主从实例所属的角色 |
Redis Monitor 命令 | 实时打印出 Redis 服务器接收到的命令,调试用 |
Redis Command Getkeys 命令 | 获取给定命令的所有键 |
Redis Client Getname 命令 | 获取连接的名称 |
Redis Config Resetstat 命令 | 重置 INFO 命令中的某些统计数据 |
Redis Command Count 命令 | 获取 Redis 命令总数 |
Redis Time 命令 | 返回当前服务器时间 |
Redis Info 命令 | 获取 Redis 服务器的各种信息和统计数值 |
Redis Config rewrite 命令 | 对启动 Redis 服务器时所指定的 redis.conf 配置文件进行改写 |
Redis Client List 命令 | 获取连接到服务器的客户端连接列表 |
Redis Client Setname 命令 | 设置当前连接的名称 |
Redis Bgsave 命令 | 在后台异步保存当前数据库的数据到磁盘 |
5.12 Redis脚本命令
命令 | 描述 |
---|---|
Redis Script kill 命令 | 杀死当前正在运行的 Lua 脚本。 |
Redis Script Load 命令 | 将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。 |
Redis Eval 命令 | 执行 Lua 脚本。 |
Redis Evalsha 命令 | 执行 Lua 脚本。 |
Redis Script Exists 命令 | 查看指定的脚本是否已经被保存在缓存当中。 |
Redis Script Flush 命令 | 从脚本缓存中移除所有脚本。 |
5.13 Redis 发布订阅命令
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
命令 | 描述 |
---|---|
Redis Unsubscribe 命令 | 指退订给定的频道。 |
Redis Subscribe 命令 | 订阅给定的一个或多个频道的信息。 |
Redis Pubsub 命令 | 查看订阅与发布系统状态。 |
Redis Punsubscribe 命令 | 退订所有给定模式的频道。 |
Redis Publish 命令 | 将信息发送到指定的频道。 |
Redis Psubscribe 命令 | 订阅一个或多个符合给定模式的频道。 |
1 | ------------订阅端---------------------- |
- 原理
个 Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息,其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端。客户端订阅,就被链接到对应频道的链表的尾部,退订则就是将客户端节点从链表中移除。
6. 事务
redis单条命令具有原子性,redis多条命令(事务)不具有原子性,Redis事务没有隔离级别的概念,本质是一组命令被序列化,按照顺序执行
命令 | 描述 |
---|---|
Redis Exec 命令 | 执行所有事务块内的命令。 |
Redis Watch 命令 | 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
Redis Discard 命令 | 取消事务,放弃执行事务块内的所有命令。 |
Redis Unwatch 命令 | 取消 WATCH 命令对所有 key 的监视。 |
Redis Multi 命令 | 标记一个事务块的开始。- |
- 正常
1 | 127.0.0.1:6379> multi |
- 丢弃事务
1 | 127.0.0.1:6379> multi |
- 编程式异常
代码有问题,事务所有命令不会执行
1 | 127.0.0.1:6379> multi |
- 运行时异常
语法问题抛出异常,1/0,其他命令可以正常执行
1 | 127.0.0.1:6379> set k1 v1 |
- 监控watch
每次提交执行exec后都会自动释放锁,不管是否成功
序号 | 锁 | 解释 |
---|---|---|
1 | 悲观锁 | 无论做什么都会加锁 |
2 | 乐观锁 | 认为什么时候都不会出现问题,所以不会上锁!获取version,更新的时候比较version |
- 正常
1 | 127.0.0.1:6379> set k1 100 |
- 多个连接进行修改
解决办法:unwatch解锁获取最新值,然后再加锁进行事务。
1 | 127.0.0.1:6379> watch money # money上锁 |
1 | 127.0.0.1:6379> INCRBY money 500 # 修改了线程一中监视的money |
1 | 127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败 |
7. 实例
7.1 Jedis
SpringBoot 一般已经不使用该技术,因为线程不安全,一般使用lettuce,jedis所有方法和cli操作的命令相同熟悉即可
1 | <!--fastjson1.2.61以下版本有漏洞--> |
1 |
|
7.2 SpringBoot和Redis
- 概述
- SpringBoot操作数据: Spring-data jpa jdbc mongdb redis
- springboot 2.x后 ,原来使用的 Jedis 被 lettuce 替换。
1 | //LettuceConnectionFactory |
jedis:采用的直连,多个线程操作的话,是不安全的。如果要避免不安全,使用jedis pool连接池!更像BIO模式
lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
- Redis在SpringBoot中的自动配置
- 找到org.springframework.boot.autoconfigure包下的spring-boot-autoconfigure-2.5.0.jar!\META-INF\spring.factories,搜索redis
- 存在一个xxxAutoConfiguration和RedisProperties
- 若要修改配置查看RedisProperties
1 | @ConfigurationProperties( |
- 测试
- 导入依赖
1 | <!--操作redis--> |
- 配置
1 | spring.redis.host=127.0.0.1 |
- 自定义序列化模板
1 |
|
- 编写代码
1 |
|
7.3 封装lettuce命令
1 | package com.xxy.utils; |
8. Redis.config
8.1 前述
- units单位
不区分大小写
1 | # 1k => 1000 bytes |
- INCLUDES包含
相当于导入其他文件import
1 | # include .\path\to\local.conf |
- GENERAL通用跳到基础配置表格
[表格]:
- SNAPSHOTTING快照
持久化,redis是内存数据库,数据容易断电就失
1 | # 多少秒进行了多少操作,就持久化到文件.rdb.aof |
REPLICATION 主从复制
SECURITY安全
1 | requirepass root |
- LIMITS限制
1 | maxclients 10000 |
- APPEND ONLY MODE 模式 aof配置(持久化)
1 | appendonly no #默认是不开启aof模式,默认使用rdb方式 |
8.2 持久化RDB
- RDB(Redis DataBase)
在指定的时间间隔内将内存中的数据集快照写入磁盘,就是Snapshot快照,它恢复时是将快照文件直接读到内存里,默认情况下, Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。文件名可以在配置文件中进行自定义。
- RDB工作原理
Redis 调用forks。同时拥有父进程和子进程。在进行
RDB
的时候,**redis
** 的主线程是不会做io
操作的,主线程会fork
一个子线程来完成该操作;子进程将数据集写入到一个临时 RDB 文件中。
当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
- 触发条件
- save的规则满足的情况下,会自动触发rdb规则,,但是会阻塞,不接受其他操作
- 执行flushall命令,也会触发我们的rdb规则
- 退出redis,也会产生dump.rdb文件
- 恢复rdb文件
- 只需要将rdb文件放到我们redis启动目录就可以,redis启动的时候会自动检查dump.rdb恢复数据
1 | # redis启动目录 |
- 优缺点
序号 | 优点 | 缺点 |
---|---|---|
1 | 适合大规模数据恢复 | 需要一定的时间间隔进行操作,如果redis意外宕机了,这个最后一次修改的数据就没有了 |
2 | 对数据的完整性要求不高 | fork进程的时候,会占用一定的内容空间 |
8.3 持久化AOF
- AOF(Append Only File)【仅仅追加命令模式】
记录命令history,以日志的形式来记录每个写的操作,需要恢复时追加命令但不可以改写文件,然后执行,默认文件无限追加,文件超过配置文件里设置大小,会重写
- 开启步骤
- 修改配置文件
1 | appendonly yes |
- 重启Redis
1 | # 查看进程 |
- aof文件出现错误
1 | redis-check-aof --fix |
- 优缺点
序号 | 优点 | 缺点 |
---|---|---|
1 | 每一次修改都会同步,文件的完整性会更加好 | 如果每秒同步一次,可能会丢失一秒的数据,如果从不同步,效率最高 |
2 | 相对于数据文件来说,aof远远大于rdb,修复速度比rdb慢! | |
3 | Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化 |
8.4 RDB和AOF对比
公司一般混合使用RDB和AOF
名称 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
9. Redis主从复制(集群)
- 定义
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。至少一主二从,主机只能写,从机只能读
- 集群解决问题
- 单台服务器难以负载大量的请求
- 单台服务器故障率高,系统崩坏概率大
- 单台服务器内存容量有限
- 集群作用
- 数据冗余
- 故障恢复
- 负载均衡:在主从复制的基础上,配合读写分离
- 高可用基石:主从复制还是哨兵和集群能够实施的基础
- 环境配置
- 查看当前redis信息
1 | # 查看当前库的信息 |
复制配置文件,修改对应信息
1、端口
2、pid名字
3、log文件名字
4、dump.rdb名字
配置一主二从,默认每台Redis服务器都是主节点
有密码的去从机的配置文件加上masterauth 密码,永久配置需要到配置文件中配置,命令配置是暂时的,命令配置重启后会变回主机
- 命令
1 | # 配置主机号和端口,把自己作为从机 |
- 配置文件
1 | # 配置主机号和端口,把自己作为从机 |
测试结果分析
主机断开连接,从机返回的是空
配置后,master将传送整个数据文件到slave ,并完成一次完全同步。
- 全量复制:
slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
- 增量复制:
:Master继续将新的所有收集到的修改命令依次传给slave,完成同步但是只要是重新连接master,一次完全同步(全量复制)将被自动执行!我们的数据一定可以在从机中看到
- 当主机断电宕机后,默认情况下从机的角色不会发生变化 ,集群中只是失去了写操作,当主机恢复以后,又会连接上从机恢复原状。
- 两种方式可以产生新的主机:
- 从机手动执行命令
slaveof no one
,这样执行以后从机会独立出来成为一个主机 - 使用哨兵模式(自动选举)
10. 哨兵模式
10.1 定义
当主服务器宕机后自动选举主机,原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例
10.2 作用
- 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
10.3 测试
- 配置哨兵配置文件sentinel.conf
1 | # sentinel monitor 被监控的名称 host port 主机没了投票数 |
- 启动哨兵
1 | [root@localhost bin]# redis-sentinel sentinel.conf |
- 断开主机,发现127.0.0.1 6379当了主机了,果主机此时回来了,只能归并到新的主机下,当做从机
10.4 优缺点
序号 | 优点 | 缺点 |
---|---|---|
1 | 哨兵集群,基于主从复制模式,所有主从配置优点,它都有 | Redis不好在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦 |
2 | 主从可以切换,故障可以转移,系统的可用性会更好 | 实现哨兵模式的配置其实是很麻烦的,里面有很多选择! |
3 | 哨兵模式就是主从复制模式的升级,手动到自动,更加健壮! | 主机回来,只能归并到新的主机下,当做从机 |
10.5 配置文件
1 | # Example sentinel.conf |
11. Redis缓存穿透和雪崩
11.1 缓存穿透(缓存查不到)
- 定义
在默认情况下,用户请求数据时,会先在缓存(Redis)中查找,若没找到即缓存未命中,再在数据库中进行查找,数量少可能问题不大,可是一旦大量的请求数据(例如秒杀场景)缓存都没有命中的话,就会全部转移到数据库上,造成数据库极大的压力,就有可能导致数据库崩溃。网络安全中也有人恶意使用这种手段进行攻击被称为洪水攻击。
- 解决方案
- 布隆过滤器
对所有可能查询的参数以Hash的形式存储,以便快速确定是否存在这个值,在控制层先进行拦截校验,校验不通过直接打回,减轻了存储系统的压力
- 缓存空对象
一次请求若在缓存和数据库中都没找到,就在缓存中方一个空对象用于处理后续这个请求。缺点:存储空对象也需要空间,大量的空对象会耗费一定的空间,存储效率并不高。解决这个缺陷的方式就是设置较短过期时间,对于保持一致性业务会有影响
11.2 缓存击穿(量大,缓存过期)
- 定义
相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。
- 解决方案
- 设置热点数据永不过期
这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。
- 加互斥锁(分布式锁)
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。这样对锁的要求就十分高。
11.3 缓存雪崩
- 定义
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。
- 解决方案
- redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群
- 限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
- 数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
12. Redis和数据库一致性
- 先更新数据库,再更新redis(弃)
如果先更新数据库成功,接着更新redis失败,那么会造成数据不一致,所以这种方法舍弃
- 先更新redis,在更新数据库(弃)
如果更新reids成功,更新数据库失败,那么同样会造成数据不一致
- 先更新数据库,再删除redis
这种方案,同样会造成数据不一致的问题,但是相比上两个方案,如果他设置key的过期时间,那么保证了数据的最终一致性。如果在更新数据库后删除redis失败,又未设置redis过期时间。那么会造成数据不一致。
如果线程A更新数据库,正准备更新redis时。线程B在更新线程A更新redis前获取了redis中的数据,那么其他数据拿到的数据还是旧数据,如果删除redis失败也会造成数据不一致
- 解决方案:
mysql和redis设置事务,在发生异常时回滚数据
redis设置重试机制,在删除失败后进入重试模式
先删除redis,再更新数据库()
如果线程A删除了redis,正准备更新数据库。线程B查询了redis没有之后,查询了数据库的旧数据,并且把它写到redis。之后线程A才更新数据成功,会出现数据库和redis的数据不一致
- 延迟双删
线程A在删除redis以及更新数据库后,睡眠一段时间后,再次删除reids中的数据。这个睡眠时间得大于一次查询的时间。
- (延迟双删)先删除 Redis,再写 MySQL,再删除 Redis(优)
- 先写 MySQL,通过 Binlog,异步更新 Redis
这个方案,比如 binlog + kafka会保证 MySQL 和 Redis 的最终一致性,但是如果中途请求 B 需要查询数据,如果缓存无数据,就直接查 DB;如果缓存有数据,查询的数据也会存在不一致的情况。所以这个方案,是实现最终一致性的终极解决方案,但是不能保证实时性。
- 个人建议
实时一致性方案:采用“先写 MySQL,再删除 Redis”的策略,这种情况虽然也会存在两者不一致,但是需要满足的条件有点苛刻,所以是满足实时性条件下,能尽量满足一致性的最优解。
最终一致性方案:采用“先写 MySQL,通过 Binlog,异步更新 Redis”,可以通过 Binlog,结合消息队列异步更新 Redis,是最终一致性的最优解。
13. 总结
本次学习了Redis,对Redis有了基础的了解,会不断进行学习