Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库[NoSQL],并提供多种语言的API。
redis性能非常好,官方提供测试数据:50个并发执行10万个请求,读的速度是11万次/s,写的速度是8.1万次/s。
Redis网站:http://www.redis.net.cn/download/:下载及安装
下载redis ,把redis包复制到linux,如/opt/mysoft/myfile
redis是c++写的,下的是redis源码包的压缩包,所以要安装c++环境
yum install gcc-c++
解压redis包。
tar -zxvf redis包 -C 要解压到的目录
## 把redis解压到 /opt/mysoft/devsoft/目录下
tar -zxvf redis-5.0.4.tar.gz -C /opt/mysoft/devsoft/
编译和编译安装
## 1 进到redis解压目录
cd /opt/mysoft/devsoft/redis-5.0.4/
## 2 编译源码包(在解压目录执行make)
make
## 3 编译安装,PREFIX指定安装到的目录(安装到/usr/local/redis 目录下)
方法1 : make install PREFIX=/usr/local/redis
这里多了一个关键字 PREFIX= 这个关键字的作用是编译的时候用于指定程序存放的路径。
比如我们现在就是指定了redis必须存放在`/usr/local/redis`目录。
假设不添加该关键字Linux会将可执行文件存放在/usr/local/bin目录,
库文件会存放在`/usr/local/lib`目录。配置文件会存放在`/usr/local/etc`目录。
其他的资源文件会存放在usr/local/share目录。
这里指定号目录也方便后续的卸载,后续直接rm -rf /usr/local/redis 即可删除redis
这个解释参考;【未亲测】
方法2:(亲测)
cd src // 进到src目录(假设安装目录为 /user/local/redis
make install // 执行编译安装,会将redis-server,redis-cli等安装到src目录下
为了方便维护,在【安装目录】新建 bin目录,并将安装的redis移动到bin目录
mkdir bin
cd src
mv redis-server redis-cli redis-sentinel redis-check-aof redis-check-rdb redis-benchmark /usr/local/redis/bin
配置环境变量:vi /etc/profile ,添加path变量到/usr/local/redis/bin目录
## 1. 在/etc/profile文件中添加下面这三行
#################REDIS_HOME##################
export REDIS_HOME=/usr/local/redis
export PATH=$PATH:$REDIS_HOME/bin
## 2. 重新加载配置文件
source /etc/profile
配置redis-server在后台启动
现在启动redis-server时,会在linux窗口上启动,一直占用着窗口,而不是后台启动。所以要修改redis配置,使redis服务在后台运行
## 1 修改redis解压目录下的redis.conf文件
# 进入解压目录
cd /opt/mysoft/devsoft/redis-5.0.4
# 编辑redis.conf文件。然后输入/daemonize 搜索关键字,将daemonize no 改成daemonize yes
vi redis.conf
## 2 将redis.conf文件 复制到 redis安装目录(也可以先复制,再改安装目录中的redis.conf文件)
cp redis.conf /usr/local/redis/
## 3 启动redis服务器端(注意:启动时要指定redis.conf配置文件)
redis-server /usr/local/redis/redis.conf
配置redis可以被外部访问
注意:必须先启动服务器端,客户端才可以连接
# 使用默认配置启动redis服务器
redis-server
# 使用 指定配置文件 启动redis服务器
redis-server /usr/local/redis/redis.conf
命令行启动客户端:redis-cli
# 连接127.0.0.1:6379 的redis服务器端。下面这句等价于 redis-cli -h 127.0.0.1 -p 6379
redis-cli
# 连接指定redis服务器端(需要配置redis可以被外部访问,注释bind,修改protected-mode no)
redis-cli -h ... -p 6379
-h:指定访问的redis服务器的ip地址
-p:指定访问的redis服务器的port端口
-a: 指定登录密码(注意:修改/etc/redis.conf 下的 #requirepass foobared 去掉#,foobared 改为要设置密码,重启)
--raw: 解决中文乱码问题
使用redis的java客户端,jedis连接。
如:注意,先jedis的包
@Test
public void testJedis() {
//创建一个Jedis的连接
Jedis jedis = new Jedis("127.0.0.1", 6379);
//密码认证 如果设置了密码,就需要进行认证
jedis.auth("offcn123");
//执行redis命令
jedis.set("mytest", "hello world, this is jedis client!");
//从redis中取值
String result = jedis.get("mytest");
//打印结果
System.out.println(result);
//关闭连接
jedis.close();
}
# 关闭当前连接的服务器端
redis-cli shutdown
quit
exit
Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁
默认redis中的数据是永不过期。即ttl key 结果为-1
设置key的生存时间(秒):EXPIRE key seconds
EXPIRE key seconds 设置key在seconds秒后自动删除
如: expire key1 60 设置key1生存时间为60秒
设置key的生存时间(毫秒):PEXPIRE key milliseconds
PEXPIRE key milliseconds 设置key在milliseconds毫秒后自动删除
如: pexpire key1 1000 设置key1的生存时间为1秒,1000毫秒
查看key的剩余生存时间:TTL key
注意:ttl如果返回值为正数(秒),表示key剩余的生存时间为该时间,如果为-1表示永久不过期,如果为-2表示是不存在的key!
TTL key 查看key剩余的生存时间
如: ttl key1 查看key1剩余的生存时间
清除生存时间(设置为永不过期):PERSIST key
PERSIST key 清除生存时间,即取消自动删除,设置为永不过期
如: persist key1 取消key1的生存时间,使其为永不过期
ttl key1 结果为:-1
keys [pattern]
:如keys *
查看所有的key,keys k*
查看所有k开头的key。
exists [key]
:判断key是否存在,存在结果为1,不存在为0。可以转为java的boolean类型
exists [key ...]
:判断是否存在key …,存在n条结果为n
# 产看所有的key
127.0.0.1:6379> keys *
1) "key2"
2) "key3"
3) "key1"
# 判断key2是否存在,因为存在,所以返回1.如果不存在则返回0
127.0.0.1:6379> exists key2
(integer) 1
# 判断key1 key2 key3 key4 key5 是否存在,因为只存在3条,所以返回3
127.0.0.1:6379> exists key1 key2 key3 key4 key5
(integer) 3
127.0.0.1:6379>
del key
:删除key为key的key
del key...
:删除多个key的数据
# 删除 key1
127.0.0.1:6379> del key1
(integer) 1
# 判断key1是否存在。结果为0 表示不存在
127.0.0.1:6379> exists key1
(integer) 0
# 删除key1 key2 key3。结果为成功删除的条数
del key1 key2 key3
rename key_old key_new
:将key_old的key修改为key_new
# 将key2重命名为newKey2
127.0.0.1:6379> rename key2 newKey2
OK
# 判断是否修改成功
127.0.0.1:6379> get newKey2
"value2"
127.0.0.1:6379> get key2
(nil)
type key
:判断key的数据类型
# key1为string类型
127.0.0.1:6379> type key1
string
# key2位hash类型,相当于map
127.0.0.1:6379> type key2
hash
# key3 为list类型,列表
127.0.0.1:6379> type key3
list
# key4 为set类型,集合
127.0.0.1:6379> type key4
set
# key5 为zset类型,sortableSet
127.0.0.1:6379> type key5
zset
redis有没有什么方法使不同的应用程序数据彼此分开同时又存储在相同的实例上呢?就相当于MySQL数据库,不同的应用程序数据存储在不同的数据库下。
redis下,数据库是由一个整数索引标识,而不是由一个数据库名称。默认情况下,一个客户端连接到数据库0。redis配置文件中下面的参数来控制数据库总数:
修改/etc/redis.conf
找到:
databases 16
通过修改后面的数量可以设置库的数量。
select 1 :切换到索引为1的库中,默认是在0库中
select 2 :切换到索引为2的库中
move key dbid
:将key的数据移动到指定dbid的库中
注意:如果指定的库中已存在这个key,将移动失败
# 把key1移动到 1库中
127.0.0.1:6379> move key1 1
(integer) 1 结果为1:成功执行1条
# 切换到 1 库中,查看是否移动成功
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "key1"
127.0.0.1:6379> ping
PONG //看到这个字符串显示表示服务器存活正常
127.0.0.1:6379> ping 123
123 //原样输出ping后的字符串
127.0.0.1:6379> echo zhangsan
"zhangsan"
127.0.0.1:6379> quit
127.0.0.1:6379> exit
flushdb 直接输入就行了,当前库的所有数据就被清空了
flushall 直接就把所有库中的数据清空了
# 先获取所有的key
127.0.0.1:6379> keys *
1) "key2"
2) "key3"
3) "key5"
4) "key1"
5) "key4"
# 查看当前库中key的个数
127.0.0.1:6379> dbsize
(integer) 5
# 在客户端直接输入info,就可以查看当前客户端连接的redis服务器端的信息。
# 比如版本,模式(单例,集群),连接的客户端数量...
info
# info 后可以接section,如下:只看replication部分的信息。它是有很多部分的。
info replication
注意:必须先订阅,再发布。可以同时订阅多个频道。
实际开发中基本不用redis做消息队列
subscribe c1 c2
publish c1 'message'
psubscribe [pattern]
如psubscribe a*
订阅a开头的频道其中c表示channel:频道
具体看下边的帖子:
https://blog.csdn.net/w05980598/article/details/80444717
https://www.runoob.com/redis/redis-pub-sub.html
在/usr/local/redis/bin目录下看到:
redis-benchmark
: redis性能测试工具
redis-check-aof
:AOF文件修复工具
redis-check-rdb
:RDB文件检查工具
redis-cli
:运行Redis客户端
redis-sentinel
: Redis软连接指向redis-server。sentinel:哨兵
redis-server
: 运行Redis服务器端
Redis中存储数据是通过key-value存储的,对于value的类型有以下5种:
关于key的定义需要注意以下几点:
1、key不要太长,最好不要超过1024个字节,太长消耗内存降低查询效率
2、key不要太短,太短降低key的可读性
3、在项目中key最好有一个统一的命名规范
4、redis中的命令语句中,命令是忽略大小写的,而 key是不忽略大小写 的。
字符串类型是Redis中最为基础的数据存储类型,它在Redis中是二进制安全的,这便意味着该类型可以接受任何格式的数据,如JPEG图像数据或Json对象描述信息等。 在Redis中字符串类型的Value最多可以容纳的数据长度是512M
命令:
set key value
:给指定的key设置对应的value
get key
:根据指定的key取出该key对应的值
mset key1 value1 key2 value2...
:同时给多个键指定相应的值。(multi set)
mget key1 key2...
:同时获取多个键对应的值(multi get)
# 设置(添加)key1的值为value1。如果key1存在就修改其value,如果不存在就添加key1
127.0.0.1:6379> set key1 value1
OK
# 获取key1的值
127.0.0.1:6379> get key1
"value1"
# 批量设置(添加)key的值
127.0.0.1:6379> mset key1 value11 key2 value2 key3 value3
OK
# 批量获取key的值,这是key1的值已经被修改了。
127.0.0.1:6379> mget key1 key2 key3
1) "value11"
2) "value2"
3) "value3"
getset key newvalue
:先将该key对应的值获取到,然后再给该key设置一个新值。(注意:本次获取的是修改之前的值)
# 获取key1的值
127.0.0.1:6379> get key1
"value11"
# 获取key1当前的值,并给他赋值为value22
127.0.0.1:6379> getset key1 value22
"value11"
# 获取key1当前的值
127.0.0.1:6379> get key1
"value22"
del key
:删除key为key的数据
# 删除key1,1表示成功删除了一条。
127.0.0.1:6379> del key1
(integer) 1
# 判断key1是否存在,0表示不存在
127.0.0.1:6379> exists key1
(integer) 0
incr key
:表示给key对应的value值增加1,要求:key对应的value必须是数值类型(数字类型的string)(incr=increase:增加)incrby key increment
:表示给key对应的value值增加指定的幅度(结果为value+increment)。要求:key对应的value必须是数值类型decr key
:表示给key对应的value值减1。(decrease:减少)decrby key decrement
:表示给key对应的value值减少指定的幅度(结果为value-decrement)案例:
# 添加key为num value为10的一条记录
127.0.0.1:6379> set num 10
OK
# num的value加1
127.0.0.1:6379> incr num
(integer) 11
# num的value加 5
127.0.0.1:6379> incrby num 5
(integer) 16
# num的value减1
127.0.0.1:6379> decr num
(integer) 15
# num的value减6
127.0.0.1:6379> decrby num 6
(integer) 9
append key str
:将key的value后追加字符串str
127.0.0.1:6379> get key1
"hello"
127.0.0.1:6379> append key1 liming
(integer) 11 11是拼接上字符串后的总长度
127.0.0.1:6379> get key1
"helloliming"
1.统计网站访问次数
tomcat集群指向同一条数据,每次访问都调用incr
2.生成数据表的id值
# 设置product:id 值从1开始,每次添加产品都调用incr来获取id。
# 用于纵向分表。即多个库里放同样的表,要保证每个库中的id不能重复
127.0.0.1:6379> set products:id 1
OK
127.0.0.1:6379> incr products:id
(integer) 2
Redis中的Hash类型key为String类型,value为map容器。所以该类型非常适合于存储值对象的信息。如Username、Password和Age等。如果 Hash中包含很少的字段,那么该类型的数据也将仅占用很少的磁盘空间。每一个Hash 可以存储4294967295个键值对。
存储模型如下图:key 对应 value,而value是map,也是key-value形式。为了区分,value中的key用filed(字段)代替
hset的时候,如果key不存在会创建相应的key,如果存在会修改这个key
hset key field value
:给某个key添加一个field-value值hmset key field1 value1 field2 value2
:给某个key同时设置多个field-value1。(hmset=hash multi set。set多个)hsetnx key field value
:当某个key的field不存在的时候,给它设置一个值。如果存在,不做修改。(hsetnx=hash set not exist,即设置不存在的值)# 添加一个person:001,并设置他value的属性username为zhangsan
127.0.0.1:6379> hset person:001 username zhangsan
(integer) 1
# 添加person:002 ,并给他的value的多个字段设置值。value为map,给map赋值多个field-value。即map为{username=lisi,age=13,gender=1}
127.0.0.1:6379> hmset person:002 username lisi age 13 gender 1
OK
# 如果person:001的value不存在username字段,就添加username=zhnagsan1
# 但是person:001的value存在username,所以这次不赋值
127.0.0.1:6379> hsetnx person:001 username zhangsan1
(integer) 0
# person:001的value不存在age字段,所以设置value的age字段为15
127.0.0.1:6379> hsetnx person:001 age 15
(integer) 1
hget key field
:读取某个key的某个field值。(hash get)hmget key field1 field2
:同时获取某个key的多个field值。(hash multi get。即hash获取多个)hkeys key
:查看某个key的所有field。(hkeys=hash keys)hvals key
:查看某个key的所有的field对应的value。(hvals=hash values)hgetall key
:查看某个key所有的field及field对应的值。(hgetall=hash get all。获取所有的)# 获取person:001的age字段的值
127.0.0.1:6379> hget person:001 age
"15"
# 获取person:001的多个字段,username字段,age字段
127.0.0.1:6379> hmget person:001 username age
1) "zhangsan"
2) "15"
# 获取person:001的value的所有的key(字段),相当于Map的keySet
127.0.0.1:6379> hkeys person:001
1) "username"
2) "age"
# 获取person:001的map的所有的value。
127.0.0.1:6379> hvals person:001
1) "zhangsan"
2) "15"
# 获取person:001的map的键和值。类似于Map的entrySet
127.0.0.1:6379> hgetall person:001
1) "username"
2) "zhangsan"
3) "age"
4) "15"
hdel key field
:删除某个key中的某个指定的field。(hash delete:即删除某个指定的filed)
del key
:删除key及key对应的所有的field-value。(即,delete 删除某个key)
127.0.0.1:6379> hgetall person:001
1) "username"
2) "zhangsan"
3) "age"
4) "15"
127.0.0.1:6379> hdel person:001 age
(integer) 1
127.0.0.1:6379> hgetall person:001
1) "username"
2) "zhangsan"
127.0.0.1:6379>
hincrby key field increment
:给某个key的指定的某个field对应的value值加指定的幅度。(hash increase:hash增加)
没有hincr,hdecr,decrby
# 给person:001的age增加5。因为person:001的age不存在,所以创建person:001的age字段,并赋值为0+5。
127.0.0.1:6379> hincrby person:001 age 5
(integer) 5
127.0.0.1:6379> hget person:001 age
"5"
hexists key field
:判断指定的key中的filed是否存在,存在返回1,不存在返回0。(hash exists:hash 是否存在。即判断指定的filed是否存在)
127.0.0.1:6379> hexists person:001 age 判断user里面是否存在age这个字段
(integer) 1
hlen key
:查看指定这个key的所有field的数量。(hash length:hash的长度,即hash中的元素数量)
# 因为person:001的map只有username和age两个字段,所以结果为2
127.0.0.1:6379> hlen person:001
(integer) 2
存储商品信息
商品字段
【商品id、商品名称、商品描述、商品库存、商品价格】
定义商品信息的key
商品1001的信息在 Redis中的key为:[products:001]
# 存储商品信息
192.168.22.132:6379> HMSET products:1001 id 3 name apple memo ‘red apple’ count 100 price 99.9
OK
# 获取商品信息
192.168.101.3:7003> HGET products:1001 id
"3"
192.168.22.132:6379> HGETALL products:1001
1) "id"
2) "3"
3) "name"
4) "apple"
5) "memo"
6) "red apple"
7) "count"
8) "100"
9) "price"
10) "99.9"
在Redis中,List类型是按照插入顺序排序的字符串链表。和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素。在插入时,如果该键并不存在,Redis将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除。List中可以包含的最大元素数量是4294967295。
从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作也可以在常量时间内完成。然而需要说明的是,如果元素插入或删除操作是作用于链表中间,那将会是非常低效的。
注意::这里的value1、value2、value3顺序实际上反了,从左边先推进去的,实际上出现在最右边,即应该是value4,value3,value2,value1
lpush key value ...
: 从左边往key对应的list集合中推入多个value。(left push)
rpush key value ...
:从右边往key对应的list集合中推入多个value。(right push)
# left push。从左边往链表里推,所以现在的顺序是 4,3,2,1
127.0.0.1:6379> lpush list1 value1 value2 value3 value4
(integer) 4
127.0.0.1:6379> lrange list1 0 -1
1) "value4"
2) "value3"
3) "value2"
4) "value1"
# right push。从右边往链表里推,所以顺序就是1,2,3,4
127.0.0.1:6379> rpush list2 value1 value2 value3 value4
(integer) 4
127.0.0.1:6379> lrange list2 0 -1
1) "value1"
2) "value2"
3) "value3"
4) "value4"
lpop key
:表示从这个key对应的集合中最左边弹出一个元素。(left pop)
rpop key
:表示从这个key对应的集合中最右边弹出一个元素。(right pop)
查看list1中所有的元素
127.0.0.1:6379> lrange list1 0 -1
1) "value4"
2) "value3"
3) "value2"
4) "value1"
# 从最左边弹出一个,可以看出来最左边的是value4,所以把value4弹出后,剩下了3,2,1
127.0.0.1:6379> lpop list1
"value4"
127.0.0.1:6379> lrange list1 0 -1
1) "value3"
2) "value2"
3) "value1"
# 从最右边弹出一个,最右边的是value1。
127.0.0.1:6379> rpop list1
"value1"
127.0.0.1:6379> lrange list1 0 -1
1) "value3"
2) "value2"
LRANGE命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回start、stop之间的所有元素(包含两端的元素),索引从0开始。索引可以是负数,如:“-1”代表最后边的一个元素,“-2”代表倒数第二个。
lrange key start stop
: 查看指定的某个key对应的列表。
127.0.0.1:6379> lrange list2 0 -1
1) "value1"
2) "value2"
3) "value3"
4) "value4"
llen key
:查看某个key对应的集合中元素的个数(llen =》list length:list的长度,即列表中的元素个数)
# list2中有4个元素。
127.0.0.1:6379> llen list2
(integer) 4
lrem key count value
key:要操作的key,
count:要删除的个数,
value:要删除的值
添加8个元素
127.0.0.1:6379> lpush list1 value1 value2 value3 value4
(integer) 4
127.0.0.1:6379> lpush list1 value1 value2 value3 value4
(integer) 8
删除1个value1
127.0.0.1:6379> lrem list1 1 value1
(integer) 1
127.0.0.1:6379> lrange list1 0 -1
1) "value4"
2) "value3"
3) "value2"
4) "value4"
5) "value3"
6) "value2"
7) "value1"
删除2个value2
127.0.0.1:6379> lrem list1 2 value2
(integer) 2
127.0.0.1:6379> lrange list1 0 -1
1) "value4"
2) "value3"
3) "value4"
4) "value3"
5) "value1"
商品评论列表
思路:
在Redis中创建商品评论列表
用户发布商品评论,将评论信息转成json存储到list中。
用户在页面查询评论列表,从redis中取出json数据展示到页面。
# 定义商品评论列表key:
# 商品编号为1001的商品评论key【products: comment:1001】
192.168.101.3:7001> LPUSH products:comment:1001 '{"id":1,"name":"Very Goods!","date":1430295077289}'
127.0.0.1:6379> LRANGE products:comment:1001 0 -1
1) "{\"id\":1,\"name\":\"Very Goods!\",\"date\":1430295077289}"
在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加、删除或判断某一元素是否存在等操作。需要说明的是,这些操作的时间是常量时间。Set可包含的最大元素数是4294967295。
和List类型不同的是,Set集合中不允许出现重复的元素。和List类型相比,Set类型在功能上还存在着一个非常重要的特性,即在服务器端完成多个Sets之间的聚合计算操作,如unions、intersections和differences。由于这些操作均在服务端完成,因此效率极高,而且也节省了大量的网络IO开销。
set add 和 set remove
sadd key value ...
:往指定key对应的集合中添加一个或多个value成员srem key value ...
:移除指定key对应的set集合中的一个或多个value成员# 创建key为set1的set类型数据,set的成员有member1,member2,member3。
127.0.0.1:6379> sadd set1 member1 member2 member3
(integer) 3
# 获取set1的所有成员,可以看出来是无序的
127.0.0.1:6379> smembers set1
1) "member3"
2) "member1"
3) "member2"
# 删除set1集合的member1跟member3,所以只剩下了member2
127.0.0.1:6379> srem set1 member1 member3
(integer) 2
127.0.0.1:6379> smembers set1
1) "member2"
smembers key
:查看某个key对应的set集合中的所有元素
# 创建key为set1的set类型数据,set的成员有member1,member2,member3。
127.0.0.1:6379> sadd set1 member1 member2 member3
(integer) 3
# 获取set1的所有成员,可以看出来是无序的
127.0.0.1:6379> smembers set1
1) "member3"
2) "member1"
3) "member2"
sismember key value
:判断指定key对应的set集合中是否含有某个value元素,有返回1,没有返回0。(sismember:set is member,即表示,是否是set集合中的成员)
# 判断set1中是否存在member2成员
127.0.0.1:6379> sismember set1 member2
(integer) 1
127.0.0.1:6379> sadd A 1 2 3 4
(integer) 4
127.0.0.1:6379> sadd B 3 4 5 6
(integer) 4
127.0.0.1:6379> sadd C 1 5 6 7 8
(integer) 5
# A-B-C (A对B的差集的结果,对C的差集)
127.0.0.1:6379> sdiff A B C
1) "2"
# A-B (A-B跟B-A是不同的差集)
127.0.0.1:6379> sdiff A B
1) "1"
2) "2"
# B-A
127.0.0.1:6379> sdiff B A
1) "5"
2) "6"
# A∩B。AB的交集
127.0.0.1:6379> sinter A B
1) "3"
2) "4"
# A∪B。AB的并集
127.0.0.1:6379> sunion A B
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
# A∪B∪C。ABC的并集
127.0.0.1:6379> sunion A B C
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "7"
8) "8"
scard key
:获取某个key对应的集合中元素的数量(set card。set的卡片。set的成员个数,太牵强了)
# set A的成员个数
127.0.0.1:6379> scard A
(integer) 4
# set B的成员个数
127.0.0.1:6379> scard C
(integer) 5
srandmember key number
:随机读取指定key对应的集合中的number(默认为1)个元素。(set random member:set随机成员)
# 在A集合中随机获取2个成员
127.0.0.1:6379> srandmember A 2
1) "2"
2) "1"
# 在A集合中随机获取1个成员
127.0.0.1:6379> srandmember A
"3"
Sorted-Set和Set类型极为相似,它们都是字符串的集合,都不允许重复的成员出现在一个Set中。它们之间的主要差别是Sorted-Sets中的每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。然而需要额外指出的是,尽管Sorted-Set中的成员必须是唯一的,但是分数(score) 却是可以重复的。
在Sorted-Set中添加、删除或更新一个成员都是非常快速的操作,其时间复杂度为 集合中成员数量的对数。由于Sorted-Set中的成员在集合中的位置是有序的,因此,即便是访问位于集合中部的成员也仍然是非常高效的。事实上,Redis所具有的这一特征在很多其它类型的数据库中是很难实现的,换句话说,在该点上要想达到和Redis同样的高效,在其它数据库中进行建模是非常困难的。
例如:游戏排名、微博热点话题等使用场景。
zadd key [NX|XX] [CH] [INCR] score member [score member ...]
:前面的看不懂。反正是可以放多个 score和member,且是一一对应的。
zadd key1 score1 value1 score2 value2 score3 value3
:添加一个zset集合。其中value1的分数为score1 ,value2的分数score2,value3的分数score3。
注意:score在前面。member在后面。
127.0.0.1:6379> zadd zset1 10 member1 30 member2 20 member3
(integer) 3
zscore key1 value1
:查看指定的key对应集合中的某个元素的分数(score:分数)
# 添加一个zset
127.0.0.1:6379> zadd zset1 10 member1 30 member2 20 member3
(integer) 3
# 获取member1的分数
127.0.0.1:6379> zscore zset1 member1
"10"
zrem key1 value
:删除指定集合的某个成员(zrem=zset remove:删除)
# 删除zset1中的member2
127.0.0.1:6379> zrem zset1 member2
(integer) 1
127.0.0.1:6379> zrange zset1 0 -1
1) "member1"
2) "member3"
zrange key start stop [WITHSCORES]
:按分数正序(低–>高)查看指定的集合中的某些元素。加了withscores就会把元素对应的score也查出来。(zset range :zset范围 在star 和stop之间)
zrevrange key start stop [WITHSCORES]
:按着分数反序(高–>低)查看集合中的某些元素。(zset reverse range:反序 范围,即从高到底排序获取集合中的元素)
127.0.0.1:6379> zadd zset2 20 member1 15 member2 16 member3 10 member4
(integer) 4
# 按分数升序获取zset2的所有成员(0第一个,-1倒数第一个,即最后一个)
127.0.0.1:6379> zrange zset2 0 -1
1) "member4"
2) "member2"
3) "member3"
4) "member1"
# 按分数升序获取所有成员,包括分数
127.0.0.1:6379> zrange zset2 0 -1 withscores
1) "member4"
2) "10"
3) "member2"
4) "15"
5) "member3"
6) "16"
7) "member1"
8) "20"
# 按分数降序获取所有成员
127.0.0.1:6379> zrevrange zset2 0 -1
1) "member1"
2) "member3"
3) "member2"
4) "member4"
# 按分数降序获取所有成员及对应的分数
127.0.0.1:6379> zrevrange zset2 0 -1 withscores
1) "member1"
2) "20"
3) "member3"
4) "16"
5) "member2"
6) "15"
7) "member4"
8) "10"
zincrby key increment member
:给指定key的zset数据的member属性加上increment增量。
127.0.0.1:6379> zrevrange zset2 0 -1 withscores
1) "member1"
2) "20"
3) "member3"
4) "16"
5) "member2"
6) "15"
7) "member4"
8) "10"
# 给zset2的member2成员的分数加 100
127.0.0.1:6379> zincrby zset2 100 member2
"115"
商品销售排行榜
需求:根据商品销售量对商品进行排行显示
思路:定义商品销售排行榜(sorted set集合),Key为products:sellsort,分数为商品销售量。
# 写入商品销售量:
# 商品编号1001的销量是9,商品编号1002的销量是10
192.168.101.3:7007> ZADD products:sellsort 9 1001 10 1002
# 商品编号1001的销量加1
192.168.101.3:7001> ZINCRBY products:sellsort 1 1001
# 商品销量前10名:
192.168.101.3:7001> ZREVRANGE products:sellsort 0 9 WITHSCORES
https://www.hxstrive.com/subject/redis/2056.htm
https://zhuanlan.zhihu.com/p/496944314
springboot redis streams 实现消息队列
https://gitee.com/bulkall/bulk-demo/tree/master/spring-boot-mq/spring-boot-mq-redis
https://blog.csdn.net/neubuffer/article/details/17003909
1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no
2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
3. 指定Redis监听端口,默认端口为6379
port 6379
4. 绑定的主机地址
bind 127.0.0.1
5.客户端和Redis服务端的连接超时时间,默认是0,表示永不超时
timeout 300
6. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为notice
loglevel notice
7. 指定包含其它的配置文件
include /path/to/local.conf
8.保护模式默认是开启的,如果要关闭,设置为no就可以了!
protected-mode yes
9.tcp-backlog
此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, 当然此值必须不大于Linux系统定义的/proc/sys/net/core/somaxconn值,默认是511,而Linux的默认参数值是128。
当系统并发量大并且客户端速度缓慢的时候,可以将这二个参数一起参考设定。
10.tcp-keepalive
如果值非0,单位是秒,表示将周期性的使用SO_KEEPALIVE检测客户端是否还处于健康状态,避免服务器一直阻塞,官方给出的建议值是60s。
11. 日志的存储路径,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout
12. 设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id,库与库之间是隔离的!0号库中默认是带有几个键值对的,可以通过DBSIZE来查看当前数据库有几个键值对!
databases 16
13.requirepass foobared
可以设置密码
14.设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码,默认关闭
requirepass foobared
16. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 100000
17. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory
18. maxmemory-policy :当Redis的最大可用内存空间都占满时,Redis会如何处理呢?
Redis给出了6个选项,让我们自行选择:
volatile-lru
使用LRU算法,从设置了过期时间的key中选择删除
allkeys-lru
使用LRU算法,从所有key中选择删除
volatile-random
从设置了过期时间的key中随机删除
allkeys-random
从所有的key中随机删除
volatile-ttl
从设置了过期时间的key中选择最先过期的删除
noeviction
不处理,当有写操作时,直接返回错误
Redis的默认策略是 noeviction,配置项为 maxmemory-policy
对于LRU,默认情况下Redis会采集5个key,然后从中根据LRU选择一个进行删除
Redis选择5这个数,是因为5比较适中,比如选择10会很准确,但是比较耗费CUP,选择3的话会非常快,但是会降低准确度
这个数可以自己配置,配置项为 maxmemory-samples,默认值是5
19. 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb
20. 指定本地数据库存放目录
dir ./
注意:redis里的1k = 1000bytes 而不是1024bytes
Redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到磁盘来保证持久化。
redis支持二种持久化方式:
这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。(默认存储在当前目录下,config get dir可以获得!)
vi /opt/mysoft/devsoft/redis-5.0.4/redis.conf
,redis.conf。/SNAPSHOTTING
查找快照配置,可以看到以下内容) save 900 1 #900秒内如果超过1个key被修改,则发起快照保存
save 300 10 #300秒内如超过10个key被修改,则发起快照保存
save 60 10000 #60秒内,如超过10000个key被修改,则保存到dump.rdb文件
注意:rdb文件保存路径是相对于redis.conf配置文件的。
save
或者bgsave
及时保存:save
:占用窗口前台保存。有可能会阻塞读写操作!bgsave
:以后台服务方式 后台异步保存!shutdown
:关闭服务器 也会触发将数据持久化。将rdb文件拷贝到redis安装目录下,正常启动就可以恢复!
运维人员:会将rdb文件做一个备份,用于恢复数据
aof日志的全称是append only file:原理是将Reids的操作日志以追加的方式写入文件,默认是 appendonly.aof
将appendonly no 设置为appendonly yes即可!
appendfilename “appendonly.aof” 保存的文件名为:appendonly.aof
在Redis的配置文件中存在三种同步方式,它们分别是:
AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据
以下也是redis.conf的APPEND ONLY MODE配置中的部分:
命令:redis-check-aof --fix appendonly.aof
:修复appendonly.aof文件
由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
如果人为在aof文件中添加其他乱七八糟的数据,也会被修复
(redis配置文件(redis.conf):如果开启了AOF会在Redis启动时,加载aof文件,因为aof文件有更好的耐久度保证)
如果性能要求比较高,就使用RDB方式[默认的];
如果数据一致性要求比较高,就使用AOF的方式。
参考:传送门链接(redis的持久化方式RDB和AOF的区别):https://www.cnblogs.com/zxs117/p/11242026.html
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
在Redis2.8以后,从节点可以发送
psync
命令请求同步数据,此时根据主从节点当前状态的不同,同步方式可能是全量复制或部分复制。
Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是: 主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
主从复制的开启,完全是在从节点发起的;不需要我们在主节点做任何事情 。
从节点开启主从复制,有3种方式:
slaveof
--slaveof
slaveof
,则该Redis实例成为从节点。注意:如果master设置了密码,从节点需要加入:masterauth
通过slaveof
命令建立主从复制关系以后,可以通过slaveof no one
断开。
或者把主从复制的配置删了不就完事了嘛。
从节点断开复制后,不会删除已有的数据,只是不再接受主节点新的数据变化。
(因为是在同一台虚拟机上模拟的多个redis服务端,所以要修改成不同的名字跟端口号)
daemonize yes
,后台运行port 6379
改成 port ****
slaveof 主库IP 主库端口
。info replication
查看当前节点的状态【主机、从机】简单的主从复制原理总结:传送门1 : https://www.cnblogs.com/daofaziran/p/10978628.html
复杂的主从复制:传送门2 :https://www.cnblogs.com/wade-luffy/p/9639986.html
哨兵模式:解决master高可用问题
含义:如果master故障了,在其它的slave节点根据投票数决定由谁从 从库转换为主库
创建sentinel.conf。哨兵配置文件。
①.新建sentinel.conf文件,名字绝不能错(要加上所有主和从服务器)
sentinel monitor 被监控数据库名字(自己随便起名字) 127.0.0.1 6379 1
sentinel monitor 被监控数据库名字(自己随便起名字) 127.0.0.1 6380 1
sentinel monitor 被监控数据库名字(自己随便起名字) 127.0.0.1 6381 1
上面最后一个数字1,表示主机挂掉后salve投票看让谁接替成为主机,得票数多少后成为主机
②.开启后台执行:daemonize yes
开启哨兵模式
执行命令:redis-sentinel 哨兵配置文件
,根据这个配置文件开启哨兵模式。
测试:关闭主服务器,等一会,再执行info replication查看两个从服务器的role,可以发现,待一会之后,会有一台从服务器的role变成master
配置哨兵模式:https://www.cnblogs.com/kevingrace/p/9004460.html
(嗐,redis事务不好使,一般不用,用spring的就够了)
和众多其它数据库一样,Redis作为Nosql数据库也同样提供了事务机制。在redis中,MULTI/EXEC/DISCARD这三个命令是我们实现事务的基石。
1、在事务中的所有命令将会被串行化的顺序执行,事务执行期间,Redis不会再为其它客户端的请求提供服务,从而保证了事务中的所有命令
被原子的执行。
2、和关系型数据库中的事务相比,在Redis事务中如果有某一条命令执行失败,其后的命令仍然会被继续执行。
3、我们可以通过MULTI命令开启一个事务,对应关系型数据库中的“Begin Transaction”。在该语句之后执行的命令都将被视为事务之内的操作,最后我们可以通过EXEC/DISCARD命令来提交/回滚该事务内的所有操作。这两个Redis命令等同于关系型数据库中的COMMIT/ROLLBACK语句。
4、在事务开启之前,如果客户端与服务器端之间出现通讯故障并导致网络断开,其后所有待执行的语句都不会被服务器执行。然而如果网络中断事件是发生在客户端执行EXEC命令之后,那么该事物中的所有命令都会被服务器执行。
multi
:开启事务用于标记事务的开始,其后执行的命令都将被存入命令队列,直到执行EXEC时,这些命令才被原子的执行,类似于关系型数据库的begin transaction
exec
:提交事务,类似于关系型数据库的:commit
discard
:事务回滚,类似于关系型数据库中的rollback
打开2个redis连接客户端
1、步骤一:在连接1,设置num 并获取数据
192.168.22.132:6379> set num 1
OK
192.168.22.132:6379> get num
"1"
2、步骤二:在连接2,num 累加1,并获取数据
192.168.22.132:6379> incr num
(integer) 2
192.168.22.132:6379> get num
"2"
3、步骤三:在连接1,获取num数据
192.168.22.132:6379> get num
"2"
4、步骤四:在连接1,开启事物,对num多次累加数据
192.168.22.132:6379> MULTI
OK
192.168.22.132:6379> INCR num
QUEUED
192.168.22.132:6379> INCR num
QUEUED
192.168.22.132:6379> INCR num
QUEUED
192.168.22.132:6379> INCR num
QUEUED
192.168.22.132:6379> INCR num
QUEUED
5、步骤五:在连接2,获取num数据
192.168.22.132:6379> get num
"2"
6、步骤六:在连接1,提交事务
192.168.22.132:6379> EXEC
1) (integer) 3
2) (integer) 4
3) (integer) 5
4) (integer) 6
5) (integer) 7
7、步骤七:在连接2,获取num数据
192.168.22.132:6379> get num
"7"
192.168.22.132:6379> set user jack
OK
192.168.22.132:6379> get user
"jack"
192.168.22.132:6379> MULTI --开启事务
OK
192.168.22.132:6379> set user rose --设置数据为rose
QUEUED
192.168.22.132:6379> DISCARD --回滚事务
OK
192.168.22.132:6379> get user --数据依然是jack
"jack"
192.168.22.132:6379> set num 10 --初始数据是10
OK
192.168.22.132:6379> get num
"10"
192.168.22.132:6379> MULTI --开启事务
OK
192.168.22.132:6379> INCRBY num 5 --数据加5 ,num=15
QUEUED
192.168.22.132:6379> INCRBY num x --数据加x,抛出异常
QUEUED
192.168.22.132:6379> INCRBY num 5 --数据加5 ,num=20
QUEUED
192.168.22.132:6379> exec
1) (integer) 15
2) (error) ERR value is not an integer or out of range
3) (integer) 20 ---当提交事务的时候执行所有操作,如果部分操作异常将被忽略
192.168.22.132:6379> get num
"20"
REDIS缓存穿透,缓存击穿,缓存雪崩原因+解决方案:https://www.cnblogs.com/midoujava/p/11277096.html
redis的雪崩和穿透:https://blog.csdn.net/lzj3462144/article/details/78323589
大白话布隆过滤器:https://www.cnblogs.com/CodeBear/p/10911177.html
布隆过滤器:https://www.cnblogs.com/cpselvis/p/6265825.html
布隆过滤器:
就是一个很大的数组,存放0,1 (所以空间小,速度快),用来判断某个元素在集合中是否存在。
根据hash函数,算出元素的一个hash值,然后对应数组上一个位置,位置上存储0或1。如果存在存储1,不存在存储0
判断是否存在:根据hash函数算出1个hash值,看对应位置上的数是0还是1,是1就表示可能存在,是0就肯定不存在。
如果只有一个hash函数,hash冲突可能性大,所以误差很大。所以有多个hash函数,分别计算hash值,然后对应位置存储0或1 。 判断是否存在:根据多个hash函数,算出多个hash值,看这多个值所在位置存的是0还是1,如果有一个位置是0,那肯定不存在,如果全是1,就有可能存在。