本来这篇文章叫三分钟入门Redis的,发现篇幅拉的太长,就不好意思叫三分钟入门Redis了,就当学习笔记了,我的学习笔记的话,一般就是三篇: 初遇、相识、甚欢。初遇讲基本思想和基本使用,相识讲高级特性和应用,甚欢篇讲思想与实现。如果哪一篇需要一点额外的知识,则会独立出来单独成一篇文章,像写NIO学习笔记的时候,需要用到操作系统和组成原理,我就写了《操作系统与通用计算机组成原理简论》。
是什么?
Redis 是 Remote Dictionary Server , 直译为远程字典服务。
Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker
Redis是一个开源的,存储在内存中的数据结构存储器,常被用来做数据库、缓存、消息队列。
注意Redis自己认为自己是一个存储于内存中的数据结构结构存储器,再强调一遍,Redis是一个内存中的数据结构存储器。
各位明白我在强调什么了吗? 我在强调数据结构,为什么强调这个呢? 以java为例,各种各样的框架和中间件层出不穷,学着学着,就会有一种学不动的感觉了,这是正常的,但是我们一定要注重体会它的设计思想,注重基础,这里的基础就包括数据结构,这个在大学时代反复被强调的课程。
回忆一下,工作中的数据结构出现率也是很高的,MySQL索引的B+树,图数据库中的图,所以我不建议计算机专业的学生太过追求框架和潮流的技术,觉得学校不交框架就是和社会脱节了,你基础学的好,会发现学这些框架十分的简单。
有什么优点?
- 快
内存速度非常快,而Redis将数据存储在内存中,自然也快。
- 文档全
现在我学习一项新技术,一般都先去官网先看看文档,Redis的文档真是丰富,我想知道的,文档上都有。
- 结构丰富
截止当前,Redis提供的数据结构就有九种。
功能完备
多机功能:
- 复制
- Sentinel(哨兵)
- 集群
- 数据库管理
- 自动过期
- 流水线
- 事务
- Lua脚本
模块
- 持久化
- 发布与订阅
多级功能、Lua脚本、 模块、发布与订阅属于高级特性,本篇不讲。
推荐的学习资料:
Redis 官方中文网站
先安装在说
Redis推荐在Linux安装部署,其他操作系统上也能跑,只是性能不佳而已。
目前Redis最新版本是6.0.7,一般流行都落后于最新版本,我们本次选择的是5.0.9。
怎么安装,官网已经讲得很清楚了,不信你可以去看。
然后我们按照官网讲的步骤,来安装一下:
wget http://download.redis.io/rele...tar redis-5.0.9.tar.gz // 解压
mv redis-5.0.9 redis // 文件重命名
cd redis
make // 编译make test // 测试编译是否成功
src/redis-server // 启动redis 服务端 这个是前台启动,不改配置文件的话,启动后就一直卡这里,
Redis 的官方文档真的是十分丰富,你可以轻松的在Documentation中找到如何配置Redis
一般我们改配置文件,也就改三处:
- 对应的是 注释 bind 127.0.0.1
网上的很多解释是bind是用来限制IP访问的,我尝试过发现是无效的,我找到了Redis对这个属性的解释:
If the computer running Redis is directly exposed to the internet, binding to all the interfaces is dangerous and will expose the instance to everybody on the internet. So by default we uncomment the following bind directive, that will force Redis to listen only into the IPv4 loopback interface address (this means Redis will be able to accept connections only from clients running into the same computer it is running).
IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES JUST COMMENT THE FOLLOWING LINE.
如果运行在服务器上的redis并且直接向互联网暴露,绑定所有的接口是危险的,将会暴露给互联网上的所有人。
这里的接口我们可以理解为网卡,每个网卡拥有一个mac地址与IP地址相对应,bind 指定IP就是指监听这个该IP对应网卡的请求(一台服务器可以拥有多个网卡)。默认情况下,我们没有注释这段,这将会强制Redis只允许本机上的客户端连接(意思是客户端和服务端必须运行在一台主机上)
- 设置密码 搜索 requirepass 属性
Warning: since Redis is pretty fast an outside user can try up to
150k passwords per second against a good box. This means that you should
use a very strong password otherwise it will be very easy to break.
外部用户每秒可以尝试15万个密码,因为Redis十分快,如果你的密码不够健壮,他将很容易被攻破。
看到这里的我,流下来眼泪,当初Redis密码不够健壮,老是被挖矿的盯上,然后服务器CPU跑满。
- 后台启动 daemonize no 改为yes
设置我密码,我打算产生一个16位的随机字符串,保证我的密码足够健壮:
然后让Redis 服务端启动的时候按我们修改的配置文件走:
src/redis-server redis.conf
redis-cli 连接服务端
auth 密码 登陆
可能有的教程,在装Redis的时候会让你关掉防火墙,但我不建议你这么做,之前我的阿里云服务器血与泪的教训。
防火墙对6379端口放行:
(1) 向防火墙添加 Redis 端口: firewall-cmd --zone=public --add-port=6397/tcp --permanent
(2) 刷新防火墙规则: firewall-cmd --reload
(3) 验证端口,查询防火墙开放端口: firewall-cmd --zone=public --list-port
如果你不想安装
redis 官方提供了在线版本:
基本数据类型和自动过期
大体上我们学习如何使用数据结构的时候,一般也就是能存什么,怎么取,怎么存。所以下面也是讲怎么取,怎么存的。
但是我不准备对Redis的命令做百科全书式的介绍,这有违本文的初衷。
截止目前为止,共提供了九种数据类型,本文只讲八种:
- Binary-safe strings 字符串.
- Lists 列表
- Sets 集合
- Sorted sets 有序集合
- Hashes 散列
- Bit arrays 位图
- HyperLogLogs (暂时想不到好的译名)
- GEO(地理坐标)
流属于高级一点的数据结构,需要配合消息队列来讲,所以本篇不讲。
字符串
能存什么?
虽然这种数据类型叫字符串,但是可不是不同那种普通的字符串,因为这种数据类型还可以存数字、图片、视频、音频、压缩文件等更为复杂的二进制数据。
如何存?
一般操纵redis字符串命令格式如下:
单个新增:
set key value [EX seconds] [PX milliseconds] [NX|XX]
批量新增
mset key1 value1 key2 value2......
中括号内的代表的是可选参数,其他是必选参数。
EX 和 PX 代表存活时间,超出这个时间就无法获取了,EX是秒 XX是毫秒
NX代表如果不存在有变量名为name(一般来说Redis称之为键,下文我们也称之为键),
XX则表示相反,存在了我也给你覆盖。
批量新增是MX
默认情况下为XX。
例子:
单个新增:
我向Redis请求新增一个名为name的字符串,如果数据库不存在则新增成功,新增成功后,保存时间为6秒,超过6秒后会被Redis自动清理
set name 'zs' ex 6 NX
批量新增:
mset name1 zs name2 lisi
如何取
取命令操作格式:
单个取:
get key
批量取
mget key1 key2
基本操作
操纵整数
上面讲字符串可以存存数字,有同学可能会说,那肯定能啊,字符串不是包含一切嘛。
我的意思是Redis存储的字符串还可以实现自增,
我们先来做一下操作数字:
一般的命令格式:
递增
incr age
set age 20
incr age
get age
会发现age变成21了
decr age
get age
age又变成20了
做过几个之后,你会发现 Redis的命令十分简单直观,完全是英语单词的缩写。
上面的增长幅度是1,我们可以自己设定增长幅度
incrby key number
decrby key number
例子:
incrby age 2
加2
decrby age 2
减去2
操作浮点数的命令差不多: 整数是incrby浮点数是incrbyfloat。
对字符串的操作
- 获取字符串的长度
strlen age
- 截取字符串
getrange key start end
- 替换字符串上指定位置的字符
setrange key offset value
List
- Lists collections of string elements sorted according to the order of insertion. They are basically linked lists.
存储字符串元素的集合,顺序和插入顺序一致,通常基于链表
这个list像java中的List但是又不完全是。
从左边存
lpush mylist a b c d e f
创建一个名为mylist的集合,并将a b c d e f 从左侧依次放入
为什么强调从左侧呢? 因为取的时候也分左右,你可以从左边取,也可以从右边开始取。
lpush 从左边存,rpush就是从右边存。
从左边取 lpop
我们上面说List的顺序和插入顺序一致,那么你从左边开始存,最后一个字符是f,那么我按照插入的顺序从左边取,也是f。
这里的从左边取是直接字面上的翻译 left pop,如果你比较熟悉数据结构,应该会记得pop是指出栈操作。lpop也继承了出栈操作指令,返回最右端的元素,并移除该元素。
一般命令格式:
lpop key
例子:
lpop mylist
返回f。
lpop返回最右端的元素,rpop就是返回最左端的元素。
Set 无序集合
- Sets: collections of unique, unsorted string elements.
集合元素唯一,无顺序
基本命令:
- 新增 存
sadd key member
- 获取全部的元素 取
smembers key
- 查看该元素在集合中是不是存在的,不存在返回0
sismember key number
sorted Set 序集合
similar to Sets but where every string element is associated to a floating number value, called score. The elements are always taken sorted by their score, so unlike Sets it is possible to retrieve a range of elements (for example you may ask: give me the top 10, or the bottom 10).
与无序集合类似,有序集合中的元素都和一个浮点数相连,称作权值,元素按权值排序。
基本命令:
- 新增 存
zadd key score member score member
- 取
zscore key score
散列
- Hashes, which are maps composed of fields associated with values. Both the field and the value are strings. This is very similar to Ruby or Python hashes.
hash是由值和相关联的字段组成的映射,值和相关联的字段都是字符串。和Ruby、python中的hashes非常像。
- 新增 存
hmset key filed value filed value
- 取
hmget key filed field
位图
- Bit arrays (or simply bitmaps): it is possible, using special commands, to handle String values like an array of bits: you can set and clear individual bits, count all the bits set to 1, find the first set or unset bit, and so forth.
位数组(由1和0组成的数组),初始状态数组的长度为8,全为0,会自动进行扩展。
- 新增
setbit key offset value
offset是位置, 假如你给的是10,会自动再扩展一字节,也就是8个位置
- 取
getbit key offset
offset 只允许为正数
HyperLogLogs
HyperLogLogs: this is a probabilistic data structure which is used in order to estimate the cardinality of a set. Don't be scared, it is simpler than it seems... See later in the HyperLogLog section of this tutorial.
这是一种估计常常用来估计集合的基数的数据结构,听起来很高端对不对,他非常简单。
通常情况下,博客网站常常会记录访客IP,这可以用来计算博客的阅读量等等。我们当然也可以用上面讲的集合来做,但是这比较消耗内存,为了高效的计算唯一访客IP这类问题,研究人员开发了很多不同的方法,其中高效的一种就是HyperLogLog,
HyperLogLog是一个专门为了计算集合的基数而创建的概率算法,对于一个给定的集合,HyperLogLog可以返回近似的基数,近似基数可能会比实际的基数小一点或大一点,但是估算基数和实际基数的误差会处在一个合理的范围。
HyperLogLogs的优点是它计算集合近似基数的内存并不会因为集合的大小而改变。具体到实现上Redis的每个HyperLogLog只需要使用12KB的内存空间,就可以对高达2的64次方个元素进行计数,而算法的标准误差仅为0.81%。
下面我们主要介绍的就是
对集合的元素进行计数
返回集合的近似基数
一般命令格式:
创建一个HyperLogLog 并对元素进行计数
pfadd key element
返回HyperLogLog 的近似基数
pfcount key
坐标
RedisGEO是Redis在3.2版本新添加的特性,通过这一特性,用户可以将经纬度格式的地理坐标存储到Redis中,并对这些坐标执行距离计算、范围查找等操作。
常规操作:
- 存
- 取
- 算距离
存命令的一般格式:
geoadd key longitude latitude member
longitude 是经度
latitude 是度纬
取命令的一般格式:
getpos key member
算距离的命令格式:
geodist key member1 member2
例子:
GEOADD HENAN 113.2099647 23.965 pingdingshan 110.12 110.24 nanyang
-- 创建了henan这个坐标,并向里面添加了两个城市对应的经纬度
-- geopos HENAN pingdingshan
获取pingdingshan的经纬度
-- 计算nanyang 和 pingdingshan之间的直线距离
geodist HENAN pingdingshan nanyang
流水线
注意到上面在介绍Redis的数据结构时,都是单独的执行每个命令,也就是说,先将一个命令发送至服务器,等服务器执行完毕并将结果返回至客户端之后,再执行下一个命令。这种执行命令的方式和批量插入有点类似,批量插入通常有两种思路:
- 遍历,每个对象是一条SQL语句
- 还是遍历,将所有的要插入到数据库的对象组成一条SQL语句(我们常常采用这种,这种性能最优)
你可以将流水线理解为批量执行命令。
虽然Redis服务器提供了流水线特性,幸运的是绝大多数Redis客户端都提供了对流水线的支持,在java操纵Redis这一节会详细介绍。
事务
通过MULTI开启一个事务,这个命令在成功开启之后将返回ok。
当一个Redis客户端执行MULTI命令之后,就进入了事务模式,所有的命令会按顺序放入一个事务队列中,当执行exec(我们可以理解为提交事务时),所有的命令才会统一执行。
Redis也允许你丢弃事务,命令是discard。discard命令会丢弃事务队列中所有的命令。
具体的说Redis的事务具备ACID性质的A、C、I性质:
- 原子性(Atomic) : 事务中的所有命令要么全部成功,如果有一个失败,则全部失败。
- 一致性(consistent) : Redis服务器会对事务及其包含的命令进行检查,确保无论事务是否执行成功,事务本身都不会对数据库造成破坏
- 隔离性: 每个Redis客户端都拥有自己独立的事务队列,并且每个Redis事务都是独立执行的,不同的事务之间不会相互影响。
为啥Redis的事务可能不具备D(持久性呢)? 因为Redis是存储在内存中,当Redis服务器运行在特定的持久化模式之下时,Redis的事务也具备持久性。
持久化
注意Redis将数据存储在内存中,存储在内存中的风险就是当系统断电,存储于Redis中的数据就没了,当我们将Redis当做数据库来用时,这是我们不愿意看到的,为了解决这个问题,Redis向用户提供了持久化功能,也就是说Redis可以将存储于内存的数据以文件的形式存储到硬盘上。
为了满足不同的持久化需求,Redis提供了RDB持久化、AOF持久化个RDB-AOF混合持久化等多种持久化方式供用户选择,如果你不喜欢,Redis也可以完全关闭持久化功能。
RDB-AOF属于高级一点的特性,本篇不讲。
粗略的说RDB是全量持久化,AOF是增量持久化。
当客户端向Redis服务端发送save命令时,此时Redis会执行RDB持久化,Redis会遍历所有的数据库并将各个数据包含的键值对全部记录到RDB文件中。在save命令执行期间,Redis服务器不再对外提供服务,如果在Redis在之前已经执行过save命令,那么redis会用新的覆盖旧的。
我们显然是难以接受在Redis持久化的时候,Redis不再提供服务,Redis提供了另一个异步的RDB持久化方式,命令是BGSAVE,该命令会让Redis服务器创建一个子进程来做持久化,虽然是异步的,但是如果Redis本身已经占用了大量的内存,那么创建子进程就会花费更多的时间,因此在执行BGSAVE时,Redis服务端仍然会由于创建子线程而拒绝对外提供服务。
RDB的缺点是每次都是扫描全部,但是这会消耗大量的计算资源和内存资源。
AOF是增量的,服务器在每次执行完毕之后,都会以协议文本的方式将被执行的命令追加到AOF的文件末尾。
但是AOF还是不完美的,我们考虑一下,随着服务器的不断运行,被执行的命令将变得越来越多,而负责记录这些命令的AOF也会变得越来越大。
AOF通过 APPENDONLY yes命令来打开。
这也是Redis引入ADB和AOF混合持久化的原因。
java 操纵 Redis
java操纵Redis也就是通过Redis客户端来做的,类似于mysql驱动一样,只不过Redis这里叫客户端。
推荐使用的客户端会标上星,
客户端在六个月内有更新的将会被标上一个笑脸。
常规操作我们还是用maven工程,引入依赖。
jedis提供的API十分简单,原先redis的命令变成了方法。
JedisShardInfo jedisShardInfo = new JedisShardInfo("ip地址" , 6379);
jedisShardInfo.setPassword("密码");
Jedis jedis = new Jedis(jedisShardInfo);
// 操纵Hy
jedis.pfadd("hycount","ddd");
// 操纵字符串
jedis.set("name","zs");
// 获取字符串
jedis.get("name");
Pipeline piple = jedis.pipelined();
总结一下
本文主要介绍了Redis的简单使用,希望能对大家有所帮助。
参考资料:
- 《Redis使用手册》 黄健宏
- Redis的官方文档
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。