1.Redis简介
(1)什么是Redis
①Redis是一个基于内存的key-value结构数据库
②基于内存存储,读写性能高
③适合存储热点数据(热点商品、资讯、新闻)
④Redis是一个开源的内存中的数据结构存储系统,它可以用作:数据库、缓存和消息中间件
⑤它存储的value类型比较丰富,也被称为结构化的NoSql数据库。NoSql,不仅仅是SQL,泛指非关系型数据库。NoSql数据库
并不是要取代关系型数据库,而是关系型数据库的补充。
(2)Redis应用场景
①缓存
②任务队列
③消息队列
④分布式锁
(3)Redis默认有16个数据库,类似数组下标从0开始,初始默认用0号库
①使用命令 select 来切换数据库。如select 8
②统一密码管理,所有库使用统一密码
③dbsize :查看当前数据库的key的数量
④flushdb:清空当前库
⑤通杀全部库
(4)Redis是单线程+多路IO复用技术
多路复用是指使用一个线程来检查多个文件描述符(Socket)的就绪状态,比如调用select和poll函数,传入多个文件描述符,如果有一个文件描述符就绪,则返回,否则阻塞直到超时。得到就绪状态后进行真正的操作可以在同一个线程里执行,也可以启动线程执行(比如使用线程池)
与Memcache的三点不同:支持多数据类型,支持持久化,单线程+多路复用
2.Redis常用操作命令
2.1 键(key)
①keys * 查看当前库所有key
②exists key 判断某个key是否存在
③type key 查看你的key是什么类型
④del key 删除指定的key数据
⑤unlink key 根据value选择非阻塞删除(仅将key从keyspace元数据中删除,真正的删除会在后续异步操作)
⑥expire key 时间(时间单位:秒,为给定的key设置过期时间
⑦ttl key 查看还有多少秒过期,-1表示永不过期,-2表示已过期
2.2 字符串(String)
(1)String是Redis最基本的类型,可以理解成与Memcached一模一样的类型,一个key对应一个value
(2)String类型是二进制安全的,意味着Redis的String可以包含任何数据。比如jpg图片或者序列化的对象
(3)String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M
(4)String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS).是可以修改的字符串,内部结构实现上类似于
Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配
①set key value //设置指定key的值
补充:mset key1 value1 key2 value2 … // 同时设置一个或多个 key-value
②get key // 获取指定key的值
补充:mget key1 value1 key2 value2 … // 同时获取一个或多个 key-value
③setex key 过期时间 value // 设置指定key的值,并将key的过期时间设为 seconds 秒
④setnx key value // 只有在key不存在时设置 key 的值
补充:msetnx key1 value1 key2 value2 …
//同时设置一个或多个key-value对,当且仅当所有给定key都不存在(原子性,有一个失败则都失败)
⑤strlen key 获得值的长度
⑥incr key 将key中存储的数字值增1,只能对数字值操作,如果为空,新增值为1
⑦decr key 将key中存储的数字值减1
补充:incr key 对存储在指定key的数值执行原子的加1操作
所谓原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会
有任何 context switch(切换到另一个线程)
1.在单线程中,能够在单条指令中完成的操作都可以认为是“原子操作”,因为中断只能发生于指令之间
2.在多线程中,不能被其他进程(线程)打断的操作就叫原子操作。
Redis单命令的原子性主要得益于Redis的单线程
⑧getrange key 起始位置 结束位置
获得值的范围,类似于java中的subString(这里是 前包,后包)
⑨setrange key 起始位置 value
用value覆写key所存储的字符串值,从<起始位置>开始(索引从0开始)
⑩getset key value //以旧换新,设置了新值的同时获得旧值
2.3 列表list操作命令
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部或者尾部。List的数据结构为快速链表quickList,首先在列表元素比较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也就是压缩列表。它将所有的元素紧挨着一起存储,分配的是一块连续的内存。当数据量比较大时才会改成quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间。Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
①lpush/rpush key value1 value2 … // 将一个或多个值插入到列表头部/尾部
例如:lpush mylist a b c
②lrange key start stop //按照索引下标获得元素(从左到右)
例如:lrange mylist 0 -1 // 0 -1表示获取所有
③lpop/rpop key // 从左边/右边吐出一个值(值在键在,值光键亡)
④llen key // 获取列表长度
⑤brpop key1 key2 … timeout // 移出并获取列表的最后一个元素,或发现可弹出元素为止
//timeout 超时时间;列表没有元素,就会一直阻塞直到超时
⑥lindex key index 按照索引下标获得元素(从左到右)
2.4 集合set操作命令
Redis的set 是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。它的底层其实是一个
value为null的hash表,所以添加,删除,查找的复杂度都是O(1)
①sadd key member1 member2 … //向集合中添加一个或多个成员
②smembers key // 返回集合中的所有成员
③sismember key value // 判断集合key是否为含有该value值,有1,没有0
④scard key // 返回该集合的元素个数
⑤sinter key1 key2 … // 返回给定所有集合的交集
⑤sunion key1 key2 … // 返回所有给定集合的并集
⑥sdiff key1 key2 … // 返回给定所有集合的差集(key1中有的,不包含key2中的)
⑦srem key member1 member2 … // 移除集合中一个或多个成员
⑧smove value // 把集合中一个值从一个集合移动到另一个集合
⑨spop key // 随机从该集合中吐出一个值
2.5 有序集合Zset(sorted set)操作命令
Redis sorted set 有序集合是string类型元素的集合,且不允许重复的成员。每个元素都会关联一个double类型的分数(score)。
redis正是通过分数来为集合中的成员进行从小到大排序。有序集合的成员是唯一的,但分数却可以重复。
①zadd key score1 member1 score2 member2 … // 向有序集合中添加一个或多个成员,或者更新已存在成员的分数
②zrange key start stop [WITHSCORS] // 通过索引区间返回有序集合中指定区间内的成员(withscors可以带分数一起返回)
③zincrby key increment member // 有序集合中对指定成员的分数加上增量 increment
④zrem key member [member …] // 移除有序集合中的一个或多个成员
zset底层使用了两个数据结构:
(1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到对应的score值。
(2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表
2.6 哈希 hash 操作命令
Redis hash是一个String类型的filed和value的映射表,hash特别适合用于存储对象。hash类型对应的数据结构是两种:
ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable
①hset key filed value // 将哈希表key中的字段field的值设为value
②hget key field // 获取哈希表中指定字段的值
③hdel key field // 删除存储在哈希表中的指定字段
④hkeys key // 获取哈希表中所有字段
⑤hvals key // 获取哈希表中所有值
⑥hgetall key // 获取在哈希表中指定key的所有字段和值
2.7 通用命令
①KEYS pattern // 查找所有符合给定模式(pattern的)key
例如: keys *
②EXISTS key // 检查给定 key 是否存在
③TYPE key // 返回key所存储的值的类型
例如:type myset
④TTL key // 返回给定 key 的剩余生存时间,以秒为单位
例如:ttl name
⑤DEL key // 该命令用于在key存在时删除key
3.Redis的新数据类型
3.1 Bitmaps
(1)简介
现代计算机采用二进制(位)作为信息的基础单位,1个字节等于8位,例如“abc”字符串是由3个字节组成,但实际在计算机
存储时将其用二进制表示,“abc”分别对应的ASCII码分别是97、98、99,对应的二进制分别是01100001、01100010和
01100011。合理的使用操作位能够有效地提高内存使用率和开发效率。Redis提供了Bitmaps这个“数据类型”可以实现对
位的操作
①Bitmaps本身不是一种数据类型,它实际上就是字符串(key-value),但是它可以对字符串的位进行操作
②Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不太相同。可以把Bitmaps想象成一个
以位为单位的数组,数组的每个单位只能存储0和1,数组的下标在Bitmaps中叫做偏移量。
(2)格式
①setbit key offset value // 设置Bitmaps中某个偏移量的值(0或1),偏移量offset从0开始
②getbit key offset // 获取Bitmaps中某个偏移量的值
③bitcount key start end // 统计字符串从start字节到end字节比特值为1的数量
4.Redis的配置文件介绍
4.1 ###Units单位###
配置大小单位,开头定义了一些基本的度量单位,只支持 bytes,不支持 bit ,大小写不敏感
4.2 ###INCLUDES包含###
类似于jsp中的 include,多实例的情况可以把公用的配置文件提取出来
4.3 ###网络相关配置
(1)bind
默认情况下 bind = 127.0.0.1 只能接受本机的访问请求,不写的情况下,无限制接受任何ip地址的访问
(2)protected-mode
如果开启了protected-mode,那么在没有设定bind ip且没有设密码的情况下,Redis只允许接受本机的响应
(将本机访问保护模式设置no)
(3)Port:端口号,默认 6379
(4)tcp-backlog
①设置tcp的backlog,backlog其实是一个连接队列,backlog队列总和=未完成三次握手队列 + 已经完成
三次握手队列。
②在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。
注意Linux内核会将这个值减小到/proc/sys/net/core/somaxconn的值(128),所以需要确认增大/proc/sys/net/core/somaxconn和/proc/sys/net/ipv4/tcp_max_syn_backlog(128)两个值来达到想要的效果
(5)timeout
一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭。
(6)tcp-keepalive
①对访问客户端的一种心跳检测,每隔n秒检测一次。
②单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60
③如果还活着继续提供服务,不活着就释放连接
4.4 ###GENERAL通用###
(1)daemonize
是否为后台进程,设置为yes。守护进程,后台启动
(2)pidfile
存放pid文件(存放进程号)的位置,每个实例会产生一个不同的pid文件
(3)loglevel
①指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为notice
②四个级别根据使用阶段来选择,生产环境选择notice 或者warning
(4)logfile:设置日志的输出文件路径,默认为空
(5)databases
设定库的数量 默认16,默认使用0号数据库,可以使用SELECT 命令在连接上指定数据库id
4.5. ###SECURITY安全###
(1)设置密码
①默认是没有密码的,设置密码可以把下面的注释打开
②在命令行中设置密码,只是临时的。重启redis服务器,密码就还原了。
4.6 #### LIMITS限制
(1)maxclients
①设置redis同时可以与多少个客户端进行连接
②默认情况下为 10000 个客户端
③如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方
发出“max number of clients reached”以作回应。
(2)maxmemory
①建议必须设置,否则,将内存占满,造成服务器宕机
②设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以
通过maxmemory-policy来指定。
③如果redis无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么redis则会针对那些
需要申请内存的指令返回错误信息,比如SET、LPUSH等。
④但是对于无内存申请的指令,仍然会正常响应,比如GET等。如果你的redis是主redis(说明你的redis有从redis),
那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的
情况下,才不用考虑这个因素。
(3)maxmemory-policy
①volatile-lru:使用LRU算法移除key,只对设置了过期时间的键;(最近最少使用)
②allkeys-lru:在所有集合key中,使用LRU算法移除key
④volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
⑤allkeys-random:在所有集合key中,移除随机的key
⑥volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key
⑦noeviction:不进行移除。针对写操作,只是返回错误信息
(4)maxmemory-samples
①设置样本数量,LRU算法和最小TTL算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis默认
会检查这么多个key并选择其中LRU的那个。
②一般设置3到7的数字,数值越小样本越不准确,但性能消耗越小
5.Redis的发布和订阅
5.1 什么是发布和订阅
(1)Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息
(2)Redis客户端可以订阅任意数量的频道
5.2 发布订阅命令行实现
(1)打开一个客户端订阅 channel1
subscribe channel1
(2)打开另一个客户端,给channel1发布消息hello
publish channel1 hello // 返回的1是订阅者数量
(3)打开第一个客户端可以看到发送的消息
注:发布的信息没有持久化,如果在订阅的客户端收不到hello,只能收到订阅后发布的信息
6.在Java中操作Redis
5.1 使用Jedis操作Redis
1)导入相关坐标
<!-- Jedis坐标 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.8.1</version> </dependency>
(2)相关实例
public class JedisTest { @Test public void testJedis() { // 1.获取连接 Jedis jedis = new Jedis("localhost", 6379); // 2.执行具体的操作 jedis.set("username","lisi"); String value = jedis.get("username"); System.out.println(value); // 3.关闭连接 jedis.close(); } }
6.2 在Spring Boot项目使用Spring Data Redis操作Redis
Spring Data Redis 中提供了一个高度封装的类:RedisTemplate,针对jedis客户端中大量api进行了归类封装,将同一类型操作
封装为opperation接口,具体分类如下:
①ValueOperations:简单K-V操作
②SetOperations:set类型数据操作
③ZSetOperations:zset类型数据操作
④HashOperations:针对map类型的数据操作
⑤ListOperations:针对list类型的数据操作
(1)导入相关坐标
<!-- Spring Data Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
(2)Redis相关配置
spring: # Redis相关配置 redis: host: localhost port: 6379 database: 0 # 默认有16个数据库,操作的是第0号数据库 jedis: # Redis连接池配置 pool: max-active: 8 # 最大连接数 max-wait: 1ms # 连接池最大阻塞等待时间 max-idle: 14 # 连接池中的最大空闲连接 min-idle: 0 # 连接池中的最小空闲连接
(3)Redis配置类
@Configuration public class RedisConfig { @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { // 创建RedisTemplate对象 RedisTemplate<Object, Object> template = new RedisTemplate<>(); // 设置连接工厂 template.setConnectionFactory(connectionFactory); // 创建JSON序列化工具 GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); // 设置key的序列化 template.setKeySerializer(RedisSerializer.string()); template.setHashKeySerializer(RedisSerializer.string()); // 设置value的序列化 template.setValueSerializer(jsonRedisSerializer); template.setHashValueSerializer(jsonRedisSerializer); // 返回 return template; } }
(3)实例
@SuppressWarnings("all") @SpringBootTest(classes = SsmMangerApplication.class) @RunWith(SpringRunner.class) public class SpringDataRedisTest { @Autowired private RedisTemplate redisTemplate; //操作String类型数据 @Test public void testString() { redisTemplate.opsForValue().set("city", "beijing"); Object city = redisTemplate.opsForValue().get("city"); System.out.println(city); } // 操作Hash类型数据 @Test public void testHash() { HashOperations hashOperations = redisTemplate.opsForHash(); // 存值 hashOperations.put("002", "name", "xaioming"); hashOperations.put("002", "age", "20"); hashOperations.put("002", "address", "bj"); // 取值 String age = (String) hashOperations.get("002", "age"); System.out.println(age); // 获得hash结构中的所有字段 Set keys = hashOperations.keys("002"); for (Object key : keys) { System.out.println(key); // 获得hash结构中的所有值 List values = hashOperations.values("002"); for (Object value : values) { System.out.println(value); } } } //操作List类型的数据 @Test public void testList() { ListOperations listOperations = redisTemplate.opsForList(); // 存值 listOperations.leftPush("mylist", "a"); listOperations.leftPushAll("mylist", "b", "c", "d"); // 取值 List<String> mylist = listOperations.range("mylist", 0, -1); for (String value : mylist) { System.out.println(value); } // 出队列 Object element = listOperations.rightPop("mylist"); System.out.println(element); } // 操作set类型的数据 @Test public void testSet() { SetOperations setOperations = redisTemplate.opsForSet(); // 存值 setOperations.add("myset", "a", "b", "c", "a"); // 取值 Set<String> myset = setOperations.members("myset"); for (String o : myset) { System.out.println(o); } // 删除成员 setOperations.remove("myset", "a", "b"); // 取值 myset = setOperations.members("myset"); for (String o : myset) { System.out.println(o); } } // 操作ZSet类型的数据 @Test public void testZSet() { ZSetOperations zSetOperations = redisTemplate.opsForZSet(); // 存值 zSetOperations.add("myZset", "a", 10.0); zSetOperations.add("myZset", "b", 11.0); zSetOperations.add("myZset", "c", 12.0); zSetOperations.add("myZset", "a", 13.0); // 取值 Set<String> myZSet = zSetOperations.range("myZset", 0, -1); for (String s : myZSet) { System.out.print(s);//bca } // 修改分数 zSetOperations.incrementScore("myZset", "b", 20.0); // 取值 myZSet = zSetOperations.range("myZset", 0, -1); System.out.println(); for (String s : myZSet) { System.out.print(s);//cab } // 删除成员 zSetOperations.remove("myZset", "a", "b"); // 取值 myZSet = zSetOperations.range("myZset", 0, -1); System.out.println(); for (String s : myZSet) { System.out.print(s);//c } } // 通用操作 @Test public void testCommon(){ // 获取Redis中所有的key Set<String> keys = redisTemplate.keys("*"); for (String key : keys) { System.out.print(key); } // 判断某个key是否存在 Boolean haha = redisTemplate.hasKey("haha"); System.out.println(haha); // 删除指定key redisTemplate.delete("myZset"); // 获取指定key对应的value的数据类型 DataType dataType = redisTemplate.type("myset"); System.out.println(dataType.name()); } }