Redis基础知识(1)
IO多路复用
IO:I/O是Input输入/Output输出的简称,通常指数据在内部存储器(内存)
和外部存储器(硬盘、优盘)
或其他周边设备之间的输入和输出。
输入/输出是信息处理系统(计算机)与外部世界(人类或另一信息处理系统)之间的通信。
BIO:同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
NIO:异步的IO
Redis基础知识
Redis能干嘛
1.内存存储、持久化,内存中是断电即失的、所以说持久化很重要(rdb、aof)
2.效率高,可以用于高速缓存
3.发布订阅系统
4.地图信息分析
5.定时器、排行榜、计数器
特性
1.多样的数据类型
2.持久化
3.集群
4.事务
学习中需要用到的东西
基础知识
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
Redis默认有16个数据库,默认选择是第0个,默认端口是6379。
可以使用 select 进行切换数据库
Redis 是单线程的。基于内存操作,官方提供的数据是 10万+ 的QPS,完全不必同样是使用 key-value 的Memecache差。
核心:为什么单线程还这么快?
五大数据类型
String
flushdb # 清空数据
keys * # 读取全部的 key 值
set key # 设置 key 值
get key # 读取 key 值
strlen key # 获取字符串的长度
append key 'hello' # 追加字符串,如果当前 key 不存在,就相当于 set key
incr key # i++ ,自增 1
decr key # i-- , 减 1
incrby key 10 # 可以设置步长,指定增量 10
decrby key 5 # 指定减 5
getrange key 0 3 # 查看字符串范围
getrange key 0 -1 # 获取全部的字符串 跟 get key 是一样的
setrange key 1 xx # 替换指定位置开始的字符串 'hxxllo'
ttl key # 获取剩余的过期时间
# setex (set with expire) # 设置过期时间
# setnx (set if not exist) # 不存在再设置 (在分布式锁中会常常使用!)
setex key 30 "hello" # 设置 key 的值为 hello 30秒后过期
setnx key "redis" # 如果 key 不存在,创建 key,返回 1
setnx key "redis" # 如果 key 存在,创建失败,返回 0
# 批量设置多个值
mset k1 v1 k2 v2 k3 v3 # 同时设置多个值 key-value 键值形式
mget k1 k2 k3 # 同时获取多个值
msetnx k1 v1 k4 v4 # msetnx 是一个原子性的操作,要么一起成功,要么一起失败
# 对象
set use:1 {name:zhangsan,age:3} # 设置一个user:1 对象 ,值为 json 字符来保存一个对象!
# 这是一个巧妙的设计:user:{id}:{filed} ,
mset user:1:name zhangsan user:1:age 2
get user:1:name user:1:age
# getset 先get然后再set
getset key redis # 如果不存在值,则返回 nil,设置值为 redis
getset key mongodb # 如果存在值,获取原来的值,并设置新的值
String 类型的使用场景:value除了是字符串还可以是数字。
- 计数器
- 统计多单位的数量 uid:001:focusOn 0 设置为0 一直累加
- 粉丝数
- 对象缓存存储
List
基本的数据类型,列表。所有的 list 命令都是 L 开头的.
在 redis 中,我们可以把 list 玩成一个队列、栈、阻塞队列!
# 将一个值或者多个值,插入到列表的头部(左)
lpush list one
lpush list two
lpush list three
lrange list 0 -1 # 获取 list 中全部的值,
lrange list 0 -1 # 通过区间获取具体的值
rpush list righr # 将一个值或者多个值,插入到列表的尾部(右)
lpop list # 移除 list 的第一个元素
rpop list # 移除 list 的最后一个元素
lindex list 1 # 通过下标获取 list 中的某一个值
llen list # 返回 list 的长度
lrem list 1 one # 移除 list 中指定个数的 value
ltrim list 1 2 # 通过下标截取截取指定的长度,这个list已经被改变了,截断了只剩下截取的元素。例如:修建树枝,修建掉的那部分已经被砍掉了,不存在了。
# rpoplpush 移除列表的最后一个元素,将它移动到新的列表中!
rpoplpush list list1 # list 原来的列表,list1 目标列表
# lset 将列表中指定下标的值替换为另外一个值,更新操作
exists list # 先判断这个列表是否存在
lset list 0 item # 如果不存在列表 我们去更新就会报错
lset list 0 item # 如果存在 更新当前下标的值
lset list 1 item2 # 如果不存在 就会报错,例如:我们的下标越界
# 将某个具体的值 插入到列表中某个值的前面或者后面
linsert list before "hello" "word"
linsert list after "hello" "mr.wang"
小结
- 他实际上是一个链表,before Node after ,left right 都可以插入值
- 如果key不窜在,创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在
- 在两边插入或者改动值,效率最高!中间元素,相对来说效率会低一点
消息排队、消息队列(Lpush Rpop)、栈(Lpush Lpop)。
Set
set 无序不重复集合,抽随机!
sadd key "hello" # set 集合中添加元素
smembers key # 查看指定 set 的所有值
sismember key hello # 判断某一个值是否存在 set 中
scard key # 获取 set 集合中的元素个数
srem key hello # 移除某一个元素
srandmember key # 随机抽选出一个元素
srandmember key # 随机抽选出指定个数的元素
# 删除指定的key,随机删除key
spop key # 随机删除 set 集合中的元素
smove key key2 "hello" # 将一个指定的值,移动到另一个set 集合
# 数字集合类
- 差集 sdiff key1 key2
- 交集 sinter key1 key2 # 共同好友就是这样实现的
- 并集 sunion key1 key2
Hash
map集合,key-map集合。本质和string类型没有太大区别,还是一个简单的key-value
hset key k1 v1 # set 一个具体的 key-value
hget key k1 # 获取一个字段值
hmset key k1 v1 k2 v2 # set 多个 key-value
hmget key k1 k2 # 获取多个字段值
hgetall key # 获取全部的数据
hdel key k1 # 删除 hash 指定的 key 字段,对应的value值也就消失了
hlen key # 获取 hash 的字段数量
hexists key K1 # 判断 hash 中指定字段是否存在
hkeys key # 获取所有key
hvals key # 获取所有value
hincr key # i++ ,自增 1
hdecr key # i-- , 减 1
hincrby key 10 # 可以设置步长,指定增量 10
hdecrby key 5 # 指定减 5
hash 更适合对象的存储,尤其是用户信息之类的,经常变动的信息!string 更加适合字符串。
Zset
有序集合,在set的基础上,增加了一个值。
zadd key 1 one # 添加一个值
zadd key 2 two 3 three # 添加多个值
zrange key 0 -1 # 获取全部值
# 排序
zrangebyscore key -inf +inf # 显示全部数据,从小到大。负无穷到正无穷
zrevrange key 0 -1 # 从大到小 排序
zrangebyscore key -inf +inf withscores # 显示全部的用户并且附带成绩
zrangebyscore key -inf 2500 withscores # 显示小于2500的数据,什序排列
# 移除
zrem key k1 # 移除有序集合中的指定元素
zcard key # 获取有序中的个数
zcount key 1 2 # 获取指定区间的元素数量
三种特殊数据类型
geospatial 地理位置
朋友的定位,附近的人,打车距离计算?
redis 的 Geo 在Redis3.2 版本就推出了! 这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
# 规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入
# 参数 key 值(精度 维度 名称)
geoadd china:city 116.40 39.90 beijing # 添加指定城市的经度纬度
# geopos 获取当前定位:一定是一个坐标值
geopos china:city beijing # 获取指定城市的经度纬度
# geodist 返回两个给定位置之间的距离
# 单位 m km mi:英里 ft:英尺
geodist china:city beijing shanghai # 查看北京到上海的直线距离
# georadius 以给定的经纬度为中心,找出某一半径内的元素
# 附近的人?(获得所有附近的人的地址,定位!通过半径来查询)
georadius china:city 110 30 1000 km # 以指定经纬度寻找1000km内的城市,所有数据应该录入 china:city ,才会让结果更加精确
georadius china:city 110 30 1000 km withcoord # 显示1000km内他人的定位信息
georadius china:city 110 30 1000 km withdist # 显示到中间距离的位置
georadius china:city 110 30 1000 km withdist count 3 # 筛选出指定个数的结果
# 找出位于指定元素周围的其他元素
georadiusbymember china:city beijing 1000 km
# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近
geohash china:city beijng chongqing # 返回一个或多个位置元素的经纬度的字符串
GEO 底层的实现原理其实就是 Zset , 我们可以使用Zset命令来操作geo!
zrange china:city 0 -1 # 查看地图中的全部元素
zrem China:city beijng # 移除指定元素
hyperloglog
简介
Redis 2.8.9 版本就更新了 Hyperloglog 数据结构!
Redis Hyperloglog 基数统计的算法!
优点:占用的内存是固定的,2^64 不同的元素的技术,只需要废 12 KB内存!如果要从内存角度来比较的话 Hyperloglog 首选。
网页的 UV (一个人访问一个网站多次,但是还是算作一个人)
0.81% 错误率!统计 UV ,可以忽略不计
pfadd key a b c d e f g # 创建第一组元素
pfcount key # 统计 key 元素的基数数量
pfadd key2 h i j k l m # 创建第二组元素
pfmerge key3 key key2 # 合并两组元素 并集
如果允许容错,那么一定可以使用 Hyoerloglog!
如果不允许容错,就使用set或者自己的数据类型即可!
bitmaps
位存储
统计用户信息,活跃,不活跃!登录,未登录!打卡,365打卡!两个状态的,都可以使用Bitmaps!
Bitmaps 位图,数据结构!凑是操作二进制位来进行记录,就只有0 和 1 两个状态!
365天 = 365 bit 1字节 = 8bit 46个字节左右!
# 使用bitmap 来记录 周一到周日的打卡
setbit key 0 0
setbit key 1 1
setbit key 2 0
setbit key 3 0
setbit key 4 1
setbit key 5 1
setbit key 6 0
# 查看某一天是否打卡
getbit key 3
# 统计这周的打卡记录,就可以看到是否有全勤
bitcount key
事务
Redis单条命令是保证原子性的,但是事务不保证原子性!
Redis事务没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会被执行!Exec
一次性、顺序性、排他性!执行一系列的命令
正常执行事务
multi # 开启事务
set k1 v1
set k2 v2
get k1
exec # 执行事务
discard # 取消事务 事务队列中的命令都不会被执行
编译时异常,代码有问题,命令错误,事务中所有的命令都不会被执行!
运行时异常,如果事务队列中存在语法性错误,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!
监控!Watch
悲观锁:
- 很悲观,什么时候都会出问题,无论做什么都会加锁!
乐观锁
- 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
Redis.conf详解
启动的时候就通过配置文件来启动
单位
配置文件 unit单位,对大小写不敏感
包含
包含,多个配置文件,就好比 Improt ,Include
网络
bind 127.0.0.1 # 绑定的IP
protected-mode yes # 是否受保护模式
port 6379 # 端口设置
通用 GENERAL
daemonize yes # 以守护进程的方式运行,默认是no,我们需要自己开启为yes
pidfile/var/run/redis_6379.pid # 如果以后台的方式运行,需要指定一个pid文件
# 日志
loglevel notice
logfile "" # 日志的文件位置
database 16 # 默认数据库的数量
always-show-logo yes # 是否显示logo
快照
持久化,在规定的时间内,执行了多少次操作,则会持久化到文件 .rdb .aof
redis 是内存数据库,如果没有持久化,那么数据断电即失!
# 900s内,如果至少有1个key进行了修改,就进行持久化操作,依次类推
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-erroe yes # 持久化如果出错,是否还需要继续工作
rdbcompression yes # 是否压缩 rdb 文件,需要消耗一些cpu资源
rdbchecksum yes # 保存 rdb 文件的时候,进行错误的检查校验
dir ./ # rdb 文件保存的目录
REPLICATION 复制,我们后面讲解主从复制的时候讲解
SECURITY 安全
config get requirepass # 获取redis的密码
config set requirepass "123456" # 设置redis的密码
auth 123456 # 使用密码登录
限制 CLIENTS
maxclients 1000 # 设置能连接上redis的最大客户端数量
maxmemory <bytes> # redis配置最大的内存容量,默认是字节
maxmemory-policy noeviction # 内存到达上限之后的处理策略
1.volatile-lru 只对设置了过期时间的key进行LRU(默认值)
2.allkeys-lru 删除lru算法的key
3.volatile-random 随机删除即将过期的key
4.allkeys-random 随机删除
5.volatile-ttl 删除即将过期的
6.noeviction 永不过期,返回错误
APPEND ONLY 模式 aof模式
appendonly no # 默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下,rdb完全够用
appendfilename "appendonly.aof" # 持久化的文件的名字
appendfsync always # 每次修改都会 sync,消耗性能
appendfsync everysec # 每秒执行一次 sync,可能会丢失这ls的数据
appendfsync no # 不执行 sync,这个时候操作系统自己同步数据,速度最快
数据淘汰策略
maxmemory
使用maxmemory配置是为了将Redis配置为对数据集使用指定的内存量
# 查看当前配置的最大结果
config get maxmemoy
# 可以修改redis.conf文件(建议)
maxmemoy 100mb
# 也可以连接redis执行
config set maxmemoy 100mb
设置maxmemoy为0将导致没有内存限制
redis数据淘汰策略
高频面试题
redis用了半年多,发现内存占用很大。然后批量删除了一些没用的key,发现内存并没有释放。
碎片管理:重启redis,如果内存比较小,可以直接修改配置文件,代表我们可用内存的百分比剩下多少的时候,帮我们去碎片整理。如果碎片整理过于频繁会降低redis的性能。