学习内容:学习Redis(Day64)
1、Redis配置文件
2、Redis的发布和订阅
3、Redis的新数据类型
4、Redis Jedis 测试
5、Redis事务
1、Redis配置文件
(1)开头定义了一些基本的度量单位,配置大小单位,只支持bytes,不支持bit,大小写不敏感
(2)网络相关配置,找到bind变量
默认情况bind=127.0.0.1只能接受本机的访问请求,不写的情况下,无限制接受任何ip地址的访问,服务器是需要远程访问的,所以需要将其注释掉。
(3)本机访问保护模式设置,将protected-mode设置为no
(4)在宿主机Windows环境上安装RedisDesktopManager,可以用来访问虚拟机上的Redis数据库。
(5)设置密码
在Redis终端查看密码config get requirepass
,设置密码config set requirepass "123456"
,此时再执行操作会提示输入密码,输入密码auth 123456
后才能继续操作。
在命令中设置密码,只是临时的。重启redis服务器,密码就还原了。
永久设置,需要再配置文件中进行设置。在# requirepass foobared下面添加requirepass 密码。
2、Redis的发布和订阅
(1)打开一个客户端订阅channel1SUBSCRIBE channel1
,打开另一个客户端,给channel1发布消息hellopublish channel1 hello
,返回的1是订阅者数量,打开第一个客户端可以看到发送的消息。
注:发布的消息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的消息。
3、Redis的新数据类型
(1)Redis提供了Bitmaps这个“数据类型”可以实现对位的操作:
Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作。
Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量。
setbit<key><offset><value>
设置Bitmaps中某个偏移量的值(0或1)(*offset:偏移量从0开始)
getbit<key><offset>
获取Bitmaps中某个偏移量的值
(2)求集合中不重复元素个数的问题称为基数问题。
解决基数问题有很多种方案:
1.数据存储在MySQL表中,使用distinct count计算不重复个数
2.使用Redis提供的hash、set、bitmaps等数据结构来处理
以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
pfadd <key>< element> [element...]
添加指定元素到 HyperLogLog 中,将所有元素添加到指定HyperLogLog数据结构中。如果执行命令后HLL估计的近似基数发生变化,则返回1,否则返回0。
pfcount<key> [key ...]
计算HLL的近似基数,可以计算多个HLL,比如用HLL存储每天的基数,计算一周的基数可以使用7天的基数合并计算即可
pfmerge<destkey><sourcekey> [sourcekey ...]
将一个或多个HLL合并后的结果存储在另一个HLL中,比如每月活跃用户可以使用每天的活跃用户来合并计算可得
(3)geoadd<key>< longitude><latitude><member>[longitude latitude member...]
添加地理位置(经度,纬度,名称),实例:geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing
两极无法直接添加,一般会下载城市数据,直接通过 Java 程序一次性导入。
有效的经度从 -180 度到 180 度。有效的纬度从 -85.05112878 度到 85.05112878 度。
当坐标位置超出指定范围时,该命令将会返回一个错误。
已经添加的数据,是无法再次往里面添加的。
geopos <key><member> [member...]
获得指定地区的坐标值。
geodist<key><member1><member2> [m|km|ft|mi ]
获取两个位置之间的直线距离
单位:
m 表示单位为米[默认值]。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
如果用户没有显式地指定单位参数, 那么 GEODIST 默认使用米作为单位
georadius<key>< longitude><latitude>radius m|km|ft|mi
以给定的经纬度为中心,找出某一半径内的元素
4、Redis Jedis 测试
(1)Jedis所需要的jar包
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
(2)创建测试程序
package com.atguigu.jedis;
import redis.clients.jedis.Jedis;
public class Demo01 {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.241.200",6379);
String pong = jedis.ping();
System.out.println("连接成功:"+pong);
jedis.close();
}
}
Jedis-API: Key
jedis.set("k1","v1");
jedis.set("k2", "v2");
jedis.set("k3", "v3");
Set<String> keys = jedis.keys("*");
System.out.println(keys.size());
for (String key : keys) {
System.out.println(key);
}
System.out.println(jedis.exists("k1"));
System.out.println(jedis.ttl("k1"));
System.out.println(jedis.get("k1"));
Jedis-API: String
jedis.mset("str1","v1","str2","v2","str3","v3");
System.out.println(jedis.mget("str1","str2","str3"));
Jedis-API: List
jedis.lpush("mylist","v1","v2","v3");
List<String> list = jedis.lrange("mylist",0,-1);
for (String element : list) {
System.out.println(element);
}
Jedis-API: set
jedis.sadd("orders","order01");
jedis.sadd("orders","order02");
jedis.sadd("orders","order03");
jedis.sadd("orders","order04");
Set<String> smembers = jedis.smembers("orders");
for (String order : smembers) {
System.out.println(order);
}
jedis.srem("orders","order02");
Jedis-API: hash
jedis.hset("hash1","userName","lisi");
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>();
map.put("telphone","13810169999");
map.put("address","atguigu");
map.put("email","abc@163.com");
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2","telphone","email");
for (String element : result) {
System.out.println(element);
}
Jedis-API: zset
jedis.zadd("zset01",100d, "z3");
jedis.zadd("zset01", 90d,"l4");
jedis.zadd("zset01", 80d,"w5");
jedis.zadd("zset01", 70d,"z6");
Set<String> zrange = jedis.zrange("zset01", 0, -1);
for (String e : zrange) {
System.out.println(e);
}
完成验证码案例。
5、Redis事务
(1)Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard来放弃组队。
组队中某个命令出现了报告错误,执行时整个的所有队列都会被取消。
如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
(2)悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制。
乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
启用乐观锁,使用WATCH 命令对所有 key 进行监视:
在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
关闭乐观锁,使用UNWATCH 命令取消 对所有 key 的监视。
如果在执行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被执行了的话,那么就不需要再执行UNWATCH 了。
(3)Redis事务三特性
单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行。
不保证原子性
事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。