什么是Redis
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis的用处
缓存
缓存现在几乎是所有中大型网站都在用的必杀技,合理的利用缓存不仅能够提升网站访问速度,还能大大降低数据库的压力。Redis提供了键过期功能,也提供了灵活的键淘汰策略,所以,现在Redis用在缓存的场合非常多。
排行榜
很多网站都有排行榜应用的,如京东的月度销量榜单、商品按时间的上新排行榜等。Redis提供的有序集合数据类构能实现各种复杂的排行榜应用。
计数器
什么是计数器,如电商网站商品的浏览量、视频网站视频的播放数等。为了保证数据实时效,每次浏览都得给+1,并发量高时如果每次都请求数据库操作无疑是种挑战和压力。Redis提供的incr命令来实现计数器功能,内存操作,性能非常好,非常适用于这些计数场景。
社交网络
点赞、踩、关注/被关注、共同好友等是社交网站的基本功能,社交网站的访问量通常来说比较大,而且传统的关系数据库类型不适合存储这种类型的数据,Redis提供的哈希、集合等数据结构能很方便的的实现这些功能。如在微博中的共同好友,通过Redis的set能够很方便得出。
最新列表
Redis列表结构,LPUSH可以在列表头部插入一个内容ID作为关键字,LTRIM可用来限制列表的数量,这样列表永远为N个ID,无需查询最新的列表,直接根据ID去到对应的内容页即可。
地图数据
Redis支持地理位置数据的存储和查询,通过使用Redis的地理位置数据类型(Geo),你可以将地理坐标(经纬度)与其他数据关联起来。例如,你可以将商店的位置坐标存储在Redis中,并使用Geo命令在地理范围内查询附近的商店。Redis提供了计算两个地理位置之间距离的命令。你可以使用这些命令计算两个坐标之间的距离,进而进行地图分析和路径规划。
Redis为什么这么快
- **内存存储:**Redis是使用内存(in-memeroy)存储,没有磁盘IO上的开销。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1)。
- 单线程实现:( Redis 6.0以前):Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销。注意:单线程是指的是在核心网络模型中,网络请求模块使用一个线程来处理,即一个线程处理所有网络请求。
- **非阻塞IO:**Redis使用多路复用IO技术,将epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间。
- **优化的数据结构:**Redis有诸多可以直接应用的优化数据结构的实现,应用层可以直接使用原生的数据结构提升性能。
- **使用底层模型不同:**Redis直接自己构建了 VM (虚拟内存)机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
为什么Redis单线程还是快
- redis是内存型数据库
- redis特殊的数据结构
- 单线程避免锁的竞争
- io多路复用
- 单线程没有上下文的切换
Redis基本概念
数据库大小
Redis有16个数据库,默认情况下对数据库0进行操作。
在Redis中,每个数据库的默认大小是由系统内存的限制决定的,而不是有一个固定的默认大小。默认情况下,Redis会尽可能使用所有可用的系统内存。
我们可以通过maxmemory这个参数用于设置Redis服务器可以使用的最大内存量。可以在Redis配置文件(redis.conf)中设置它,或者在启动Redis服务器时通过命令行选项进行设置。
例如,设置总内存限制为1GB:maxmemory 1gb。
切换数据库
在应用程序中,选择要操作的数据库(0到15之间的数据库编号),并使用Redis的SELECT 命令切换到相应的数据库。并可以使用DBSIZE来查看当前数据库的大小。
Redis数据类型
RedisKey基本命令
|----------------|---------------|
| keys * | 查看当前数据库的所有key |
| set key | 添加一个key |
| get key | 获取key |
| type key | 查看key的类型 |
| EXISTS key | 查询是否存在key |
| move key | 移除key |
| EXPIRE key 100 | 设置过期时间为100秒 |
| ttl key | 查看剩余过期时间 |
| flushdb | 清空当前数据库 |
| flushall | 清空所有数据库 |
五种基本数据类型
String
String是最常用的一种数据类型,普通的key- value 存储都可以归为此类。其中Value既可以是数字也可以是字符串。使用场景:常规key-value缓存应用。常规计数: 微博数, 粉丝数。
|------------------------|-----------------|
| append key "test" | 为key追加一个test字符串 |
| strlen key | 获取key的长度 |
| incr key | key的值加一 |
| decr key | key的值减一 |
| incrby key n | key的值加n |
| decrby key n | key的值减n |
| GETRANGE key start end | 截取key的一部分 |
| SETRANGE key n xxx | 从n开始替换key为xxx |
| mset k1 v1 k2 v2 k3 v3 | 批量添加key |
List
List是一个有序可重复的集合,其遵循FIFO的原则,底层是依赖双向链表实现的,因此支持正向、反向双重查找。通过List,我们可以很方面的获得类似于最新回复这类的功能实现。
|-----------------------------------|--------------------------|
| LPUSH list demo | 创建list并在头部(左)放入demo |
| LRANGE list 0 -1 | 查询list所有元素 |
| LRANGE list 0 1 | 查询list索引0-1的元素 |
| RPUSH list demo2 | 在list并在尾部(右)放入demo |
| LPOP list | 删除头部元素 |
| RPOP list | 删除尾部元素 |
| LINDEX n | 获取索引为n的元素 |
| LLEN list | 获取list的长度 |
| LREM list n key | 移除list中的n个key |
| LTRIM list start end | 从start开始到end结束来截取list |
| LINSERT list a BEFORE/AFTER value | 从投开始在找到的第一个a前/后添加一个value |
Set
Set是一个无序的天然去重的集合,即Key-Set。此外还提供了交集、并集等一系列直接操作集合的方法,对于求共同好友、共同关注什么的功能。
|-----------------------|-----------------|
| sadd myset hello | 向set中添加元素 |
| SMEMBERS myset | 查看myset所有值 |
| SISMEMBER myset hello | 判断某一个值是否在myset中 |
| scard myset | 获取集合中元素的个数 |
| SRANDMEMBER myset n | 从set中随机获取n个元素 |
具体的功能实现
差集
SDIFF set1 set2
可以用于计算set1集合中有而set2集合中没有的东西。
并集
SUNION set1 set2
可以用于计算set1和set2中的所有不重复元素
交集
SINTER set1 set2
查看两个集合的相交元素,用于查找两个用户的共同好友或者共同关注的实现。
Hash
哈希类型中的 映射关系 叫作 field-value
,这里的 value
是指 field
对应的 值 ,不是 键 对应的值。可以大致理解为key - map 集合。本质上和string没有什么区别。相较于string来说,hash更适合来进行对象的存储。
|------------------------------------------|----------------------|
| hset myhash field1 gao | 设置一个具体的filed value |
| hget myhash field1 | 获取一个具体的value |
| hmset myhash field1 value1 field2 value2 | 设置多个field value |
| hmget myhash field1 field2 | 获取多个field的value |
| hgetall myhash | 获取全部的值 |
| hdel myhash field | 删除指定的field value |
| hlen myhash | 获取hash的field-value数量 |
| hexists mhash field1 | 判断是否存在,返回0 1 |
| hkeys myhash | 获取全部field |
| hvals myhash | 获取全部value |
Zset
类似于java中的TreeSet,是Set的可排序版。此外还支持优先级排序,维护了一个score的参数来实现。适用于排行榜和带权重的消息队列等场景。
|----------------------------------------|---------------------------|
| zadd score 98 a | 为score集合添加a,值为98 |
| zrangebyscore score min max | 从min到max进行从小到大的排序 |
| zrangebyscore score min max withscores | 从min到max进行从小到大的排序并输出score |
| zreverange max min | 从max到min进行从大到小的排序 |
| zcard score | 获取score的元素个数 |
三种特殊数据类型
Geospatial
主要用于存储地理位置信息,并对存储的信息进行操作,适用场景如朋友的定位、附近的人、打车距离计算等。
|----------------------------------------------|---------------------------|
| geoadd china:city 116.40 39.90 beijing | 添加北京的经纬度 |
| geodist china:city beijing shanghai km | 计算北京和上海的直线距离单位为km |
| geopos china:city beijing | 获取北京的经纬度坐标 |
| georadius china:city 110 30 1000 km | 查询这个坐标1000km范围内的元素 |
| georadius china:city 110 30 1000 km withdist | 查询这个坐标1000km范围内的元素并显示具体距离 |
| georadiusbymember china:city beijing 1000km | 查询距离北京1000km内的元素 |
Hyperlog
HyperLogLog 是一种用于统计基数的数据集合类型,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。场景:统计网页的UV(即Unique Visitor,不重复访客,一个人访问某个网站多次,但是还是只计算为一次)。统计时大概有0.81%的误差,是完全可以忽略不计的。
基数
比如数据集{ 1,3,5,7,5,7,9} ,那么这个数据集的基数集则为{1,3,5,7,9},基数(不重复元素)为5(个)
如果允许有误差--网页访问量 使用hyperlog
如果不允许有误差--共同好友 使用set
|------------------------------|-------------------------|
| pfadd mykey q w e r | 新增一个数据集 |
| pfcount mykey | 求数据集的数据量 |
| pfmerge mykey3 mykey1 mykey2 | 合并mykey1 mykey2到mykey3中 |
Bitmap
Bitmap想象成一个以位为单位数组,数组中的每个单元只能存0或者1,数组的下标在Bitmap中叫做偏移量。使用Bitmap实现统计功能,更省空间。如果只需要统计数据的二值状态,例如商品有没有、用户在不在等,就可以使用 Bitmap,因为它只用一个 bit 位就能表示 0 或 1。
打卡案例
|-----------------|------------------|
| setbit sign 0 1 | 设置周一打卡 |
| getbit sign 0 | 获取周一打卡情况 |
| bitcount sign | 统计这周打卡情况,可以指定时间段 |