Redis完整版
-
为什么要使用redis
1. 性能:我们在碰到需要执行耗时特别久,且结果不频繁变动的SQL,就特别适合将运行结果放入缓存。这样,后面的请求就去缓存中读取,使得请求能够迅速响应 2. 并发:在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问数据库。 3. 多样的数据结构存储持久化数据 最新的N个数据:通过List按自然时间排序 排行榜:利用zset 时效性数据比如验证码:expire过期 计数器 秒杀:原子性,自增方法INCR DECR 去除大量数据中的重复数据:利用set集合 构建队列:利用list集合 发布订阅系统:pub/sub模式
-
redis概述
1. Redis是一个开源的key-value存储系统。 2. 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。 3. 这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。 4. 在此基础上,Redis支持各种不同方式的排序。 5. 与memcached一样,为了保证效率,数据都是缓存在内存中。 6. 区别的是Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。 7. 并且在此基础上实现了master-slave(主从)同步
-
redis安装bin目录下的内容
- redis-benchmark:性能测试工具,可以在自己本子运行,看看自己本子性能如何
- redis-check-aof:修复有问题的AOF文件,rdb和aof后面讲
- redis-check-dump:修复有问题的dump.rdb文件
- redis-sentinel:Redis集群使用
- redis-server:Redis服务器启动命令
- redis-cli:客户端,操作入口
-
redis后台启动设置
修改redis.conf文件将里面的daemonize no 改成 yes
-
redis启动
redis-server redis.conf
-
redis客户端访问
redis-cli -p 端口
-
redis连接测试
进入客户端 执行ping 会回pong
-
redis关闭
进入客户端:shutdown
指定端口shutdown:redis-cli -p 6379 shutdown
-
redis相关介绍
-
默认16个数据库
-
使用select切换数据库
-
flushdb清空当前库
-
Redis是单线程+多路I0复用技术
简单理解就是:一个服务端进程可以同时处理多个套接字描述符。 多路:多个客户端连接(连接就是套接字描述符) 复用:使用单进程就能够实现同时处理多个客户端的连接 多线程和多进程是通过增加进程和线程的数量来并发处理多个套接字,免不了上下文切换的开销 IO 多路复用只需要一个进程就能够处理多个套接字,从而解决了上下文切换的问题
-
-
redis的五大常用数据类型
字符串(String),列表(list),集合(set),哈希(hash),有序集合(zset)
-
redis键
- keys * :查看当前库所有key(生产环境一般禁止掉)
- exist key:判断有个key是否存在
- type key:查看key的类型
- del key :删除指定的key
- expire key 10:为给定的key设置过期时间
- ttl key:查看还有多少秒过期
- select:切换数据库
- dbsize:查看当前数据库的key的数量
- flushdb:情况当前库
-
redis字符串
String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。 String类型是Redis最基本的数据类型,一个Redis中字符串value最多可以是512M
-
set 添加键值对
*NX:当数据库中key不存在时,可以将key-value添加数据库 *XX:当数据库中key存在时,可以将key-value添加数据库,与 NX参数互斥 *EX:key的超时秒数 *PX:key的超时毫秒数,与EX互斥
-
get :查询对应键值
-
append :将给定的 追加到原值的末尾
-
strlen :获得值的长度
-
incr :将 key 中储存的数字值增1 ,只能对数字值操作,如果为空,新增值为1
-
decr :将 key 中储存的数字值减1,只能对数字值操作,如果为空,新增值为-1
-
incrby / decrby <步长> :将 key 中储存的数字值增减。自定义步长。
-
mset … :同时设置一个或多个 key-value
-
mget … :同时获取一个或多个 value
-
msetnx … :同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
-
getrange <起始位置><结束位置>:获得值的范围,类似java中的substring
-
setrange <起始位置> :用 覆写所储存的字符串值,从<起始位置>开始(索引从0开始)。
-
setex <过期时间>:设置键值的同时,设置过期时间,单位秒。
-
getset :以新换旧,设置了新值同时获得旧值。
数据结构
String的数据结构为简单动态字符串(Simple Dynamic String,缩写SDS)。是可以修改的字符串,内部结构实现上类似于Java的ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配 内部为当前字符串实际分配的空间capacity一般要高于实际字符串长度len。当字符串长度小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间。需要注意的是字符串最大长度为512M
-
-
redis列表(list)
单键多值 Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边) 它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
- lpush/rpush … :从左边/右边插入一个或多个值
- lpop/rpop :从左边/右边吐出一个值。值在键在,值光键亡。
- rpoplpush :从列表右边吐出一个值,插到列表左边。
- lrange
:按照索引下标获得元素(从左到右) - lrange mylist 0 -1 0左边第一个,-1右边第一个,(0 -1表示获取所有)
- lindex :按照索引下标获得元素(从左到右)
- linsert before :在的后面插入插入值
- lrem :从左边删除n个value(从左到右)
- lset:将列表key下标为index的值替换成value
数据结构
List的数据结构为快速链表quickList。 首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,也即是压缩列表。 它将所有的元素紧挨着一起存储,分配的是一块连续的内存。 当数据量比较多的时候才会改成quicklist。 因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next Redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太大的空间冗余。
-
redis集合
Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。 Redis的Set是string类型的无序集合。它底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。 - sadd <key><value1><value2> ..... :将一个或多个 member 元素加入到集合 key 中,已经存在的 member 元素将被忽略 - smembers <key>:取出该集合的所有值 - sismember <key><value>:判断集合<key>是否为含有该<value>值,有1,没有0 - scard<key>:返回该集合的元素个数。 - srem<key><value1><value2> ....: 删除集合中的某个元素。 - spop <key>:随机从该集合中吐出一个值。 - srandmember <key><n>:随机从该集合中取出n个值。不会从集合中删除 - smove<source><destination>value:把集合中一个值从一个集合移动到另一个集合 - sinter <key1><key2>返回两个集合的交集元素 - sunion <key1><key2>返回两个集合的并集元素 - sdiff <key1><key2>返回两个集合的**差集**元素(key1中的,不包含key2中的) ### 数据结构 ``` set数据结构是dict字典 字典是用哈希表实现的
-
Redis哈希(Hash)
Redis hash 是一个键值对集合。 Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 通过 key(用户ID) + field(属性标签) 就可以操作对应属性数据了,既不需要重复存储数据,也不会带来序列化和并发修改控制的问题
- hset 给集合中的 键赋值
- hget 从集合取出 value
- hmset … 批量设置hash的值
- hexists查看哈希表 key 中,给定域 field 是否存在
- hkeys 列出该hash集合的所有field
- hvals 列出该hash集合的所有value
- hincrby 为哈希表 key 中的域 field 的值加上增量 1 -1
数据结构
Hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable
-
Redis有序集合Zset(sorted set)
Redis有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合。 不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合的成员是唯一的,但是评分可以是重复了 。 因为元素是有序的, 所以你也可以很快的根据评分(score)或者次序(position)来获取一个范围的元素。
-
zadd …
将一个或多个 member 值加入到有序集key
-
zrange
[WITHSCORES] 返回有序集 key 中,下标在
之间的元素 带WITHSCORES,可以让分数一起和值返回到结果集。
-
zrangebyscore key minmax [withscores] [limit offset count]
返回有序集 key 值介于min 之间包括等于min 的成员。有序集成员按score值递增(从小到大)次序排列。
-
zrevrangebyscore key maxmin [withscores] [limit offset count]
同上,改为从大到小排列。
-
zincrby
为元素的score加上增量 -
zrem 删除该集合下,指定值的元素
-
zcount 统计该集合,分数区间内的元素个数
-
zrank 返回该值在集合中的排名,从0开始。
数据结构
SortedSet(zset)是Redis提供的一个非常特别的数据结构,一方面它等价于Java的数据结构Map<String, Double>,可以给每一个元素value赋予一个权重score,另一方面它又类似于TreeSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围来获取元素的列表。 zset底层使用了两个数据结构 (1)hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。 (2)跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
-
-
redis配置文件
-
Units单位
配置大小单位 只支持bytes 不支持bit
-
bind
默认情况bind=127.0.0.1只能接受本机的访问请求 不写的情况下,无限制接受任何ip地址的访问 生产环境肯定要写你应用服务器的地址;服务器是需要远程访问的,所以需要将其注释掉
-
protected-mode
将本机访问保护模式设置为no
-
port
默认为6379
-
tcp-backlog
设置tcp的backlog,backlog其实是一个连接队列,backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列。 在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。
-
timeout
一个空闲的客户端维持多久关闭 0表示永不关闭
-
tcp-keepalive
对访问客户端的一种心跳检测,每个n秒检测一次。 单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60
-
pidfile
存放pid文件的位置 每个实例会产生一个不同的pid文件
-
maxclients
设置redis同时可以与多少个客户端进行连接。 如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。 默认情况下为10000个客户端。
-
maxmemory
建议必须设置,否则将内存占满会造成服务器宕机
设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定
-
maxmemory-policy
volatile-lru:使用LRU算法移除key,只对设置了过期时间的键;(最近最少使用) allkeys-lru:在所有集合key中,使用LRU算法移除key volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键 allkeys-random:在所有集合key中,移除随机的key volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key noeviction:不进行移除。针对写操作,只是返回错误信息
-
-
redis_Jedis测试
-
jedis所需要的jar包
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency>
-
创建测试程序
import redis.clients.jedis.Jedis; public class test { public static void main(String[] args) { Jedis jedis = new Jedis("192.168.137.3", 6379); String pong = jedis.ping(); System.out.println("连接成功:" + pong); jedis.close(); } }
-
-
Redis_事务_锁机制_秒杀
Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。 Redis事务的主要作用就是串联多个命令防止别的命令插队。
-
multi,exec,discard
从输入Multi命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行。组队的过程中可以通过discard
-
事务的错误处理
组队中某个命令出现的报告错误,执行时整个的所有队列都会被取消
-
事务冲突的问题
悲观锁:每次拿数据都会上锁
乐观锁:乐观锁不会拿数据时上锁,,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的
-
-
redis事务三特性
-
单独的隔离操作
事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
-
没有隔离级别的概念
队列中的命令没有提交之前都不会实际被执行,因为事务提交前任何指令都不会被实际执行
-
不保证原子性
事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
-
-
Redis持久化之RDB
redis提供了两个不同形式的持久化方式: RDB(redis database) AOF(append of file) 备份是如何执行的: Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失 优势: 适合大规模数据恢复 对数据完整性和一致性要求不高更适合使用 节省磁盘空间 恢复速度快 劣势: Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
-
Redis持久化之AOF
以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录), 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作 AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失) AOF同步频率设置: appendfsync always 始终同步,每次Redis的写入都会立刻记入日志;性能较差但数据完整性比较好 appendfsync everysec 每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。 appendfsync no redis不主动进行同步,把同步时机交给操作系统。 优势: 备份机制更稳健,丢失数据概率更低。 可读的日志文本,通过操作AOF稳健,可以处理误操作。 劣势: 比起RDB占用更多的磁盘空间。 恢复备份速度要慢。 每次读写都同步的话,有一定的性能压力。 存在个别Bug,造成恢复不能。
-
Redis主从复制
主机数据更新后根据配置和策略, 自动同步到备机的master/slaver机制,Master*以写为主,Slave以读为主
用处: 读写分离,性能拓展 容灾快速恢复 查看主机运行情况,打印主从复制信息: info replication 成为某个实例的从服务器: slaveof <ip> <port> 复制原理: Slave启动成功连接到master后会发送一个sync命令 Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行 哨兵模式(sentinel): 能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库 优先级在redis.conf中默认:slave-priority 100,值越小优先级越高 新建sentinel.conf文件,名字不能错,填写内容: sentinel monitor mymaster 127.0.0.1 6379 1 其中mymaster为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量。 启动哨兵: redis-sentinel sentinel.conf
-
Redis应用问题解决
-
缓存穿透
原因: key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库 解决: 1. 对空值进行缓存 2. 设置可访问名单 3. 采用布隆控制器:将所有可能存在的数据哈希到一个足够大的bitmaps中,一个一定不存在的数据会被 这个bitmaps拦截掉,从而避免了对底层存储系统的查询压力
-
缓存击穿
原因:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。 解决: 1. 预先设置热门数据,加大热门数据key时长 2. 实时调整:现场监控那些数据热门,实时调整过期时长
-
缓存雪崩
原因: key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。 缓存雪崩与缓存击穿的区别在于这里针对很多key缓存,前者则是某一个key 解决: 1.构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等) 2. 使用锁或队列 3. 设置过期标志更新缓存 4. 将缓存失效时间分散开
-