Redis (Remote Dictionary Server )
NoSql–不仅仅是Sql
NoSql的四大分类
- KV键值对:
- 新浪:redis
- 美团:redis+Tair
- 阿里,百度:redis+memecache
- 文档形数据库:
- MongoDB
- 是基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档
- MongDB是介于关系型数据库和非关系型数据库中间的产品
- ConthDB
- MongoDB
- 列存储数据库:
- HBASE
- 分布式文件数据库
- 图关系数据库
- 不是存图形的,而是存关系的(如朋友圈社交网络)
- Neo4j,InfoGrid;
Redis
redis能干嘛
- 内存存储,持久化,内存中是断电即失,所以需要持久化(rdb, aof)
- 效率高,可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器,计数器(浏览量!)
特性
- 多样的数据类型
- 持久化
- 集群
- 事务
…
中文官网
- redis-benchmark 是一个压力测试工具
- redis-cli 连接
- redis-server +配置文件 按配置文件启动服务
基础知识
- 默认有16个数据库 ,默认使用的是第0个数据库 可以使用
- select [index] 切换数据库
- DBSIZE 当前数据库大小
- flushdb 清除当前数据库 FULSHALL 清空全部数据库
- EXISTS [key] 判断当前key是否存在
- move [key] 移除当前key
- EXPIRE[key] 设置key的过期时间 (单位是秒) ttl [key] 查看当前key的剩余时间
- redis是单线程的
- 为什么redis的单线程的: 因为redis是基于内存操作的,CPU不是redis的性能瓶颈,redis的性能瓶颈是根据机器的内存和网络带宽。
- redis为什么这么快:redis是将所有的数据全部放在内存中的,所以使用单线程去操作效率最高。多线程(CPU会发生上下文切换,耗时的操作),对于内存来说,如果没有上下文切换,效率就是最高的。
redis的数据类型
redis的key
五大基本数据类型
string
- 操作和 java中的string有点像
- append [key] 追加
- incr [key] 值+1 decr [key] 值-1 经常用于做浏览量操作
- incrby key 步长 值增加步长 decrby key 步长 值减少步长
- 截取子串 getrange key index end 左闭右闭 end为-1 查看完整值
- 替换子串 setrange key index newstr 从index开始,替换 newstr长度
- setex [key] time 值 设值 加存活时间
- setnx [key] 值 设值 ,如果值不存在则设值成功,如果值存在 则设值失败 (相当于不能修改)
- mset k v k v … 批量设置 k v mget k k… 批量取值
- msetnx k v k v 是原子性的操作 有一个设值失败则全部失败
对象操作
- set user:1{name:zz,age:3}
- 巧妙的设计 : user:[id]:[filed]
- mset user:1:name zhangsan user:1:age 3 mget user:1- getset k v 先返回原值 再设值
list
- list可以玩成 栈,队列,阻塞队列
- 所有的list的操作命令都是 l 开头的
- lRang k begin end
- lpush k v […] 头插队列 可以类似于 栈
- Rpush k v […]
- Lpop Rpop 左弹出 右弹出
- lindex k index 通过下标获取值
- llen k 获取 k的长度
- lrem k num v 移除 list中的 num个v [从左往右移除]
- ltrim k begin end 截取 begin-end的左闭右闭 且最终k保留的值是截取的内容
- rpoplpush oldk newk 移除原列表的最右面元素 并将其左加到新列表中
- lset index v 将列表中指定下标的值替换成新的值(相当于更新操作,如果指定下标没值则会报错)
- linsert k BEFORE|AFTER v1 v2 在v1的前面|后面插入v2
list数据结构,实际上是一个链表,可以在左边或者右边插入数据
如果key不存在则创建链表,如果key存在,则新增内容。如果移除了所有值(空链表) 则不存在
可以用来做消息队列
set
- set指定都是s开头的
- sadd k v 添加元素
- sMembers k 查看 k的所有值
- sIsmember k 判断某一个值是不是在set集合中
- sCard 获取set集合中的元素个数
- sRem k v 删除 set中的指定元素
- sRandmember k num 随机输出k的num个元素 (默认是1可以不写)
- spop k 随机删除k的一个元素
- smove k1 k2 v 将一个指定的值移动到另外一个集合中
交并差集 - sDiff k1 k2 k1 - k2 的差集
- sInter k1 k2 k1和k2的交集 共同好友就是这样实现的
- sUntion k1 k2 k1和 k2 的并集
hash
- hset k mapk mapv [num 可以设置增量]
- hget k
- hmset k mk1 mv1 mk2 mv2 … set多个map
- hmget k mk1 mk2… 获取多个字段值
- hgetall k 获取全部的键跟值
- hdel k mk 删除指定的map
- hlen k 获取k中键值对的个数
- hExists k mk 判断 hash中的指定字段是否存在
- hkeys k 获取所有的key
- hvals k 获取所有的值
- hIncrby k mk num 增加num hDecrby k mk num 减少num
- hsetnx k mk v 如果存在不可以设置 存在可以设置 (相当于更新)
hash可以存储经常变更的数据,尤其是用户信息之类的
hash更适合于对象的存储,String更加适合字符串的存储
hash的底层数据结构
压缩队列 or hashtable
当数据较少的时候使用 压缩队列
当数据量较大的时候使用 哈希表
Zset (sorted set有序集合)
在set的基础上增加了个值
- zadd k score v … 可添加多个值,score表示排序的权重。
- zRange k index end
- zRevRange k index end 从大到小 进行排序 (反转)
- zRangeByScore k minscore1 maxscore2 [withscores] 顺序输出 minScore1-maxScore 里的内容(加上withscores 则输出附带成绩)
- -inf 表示负无穷 +inf表示正无穷
- zRangeByScore
- zrem k v 移除k中的v
- zcard k 获取集合中的个数
- zcount k score1 score2 获取指定区间的成员数量
可以做b站排行榜应用
底层数据结构 : skiplist(跳表)增删改查的时间复杂度是 OlogN
为什么redis中 zset底层使用了 跳表,而不是b+Tree
数据库索引设计为B+Tree主要是解决I/O读取问题
因为B+树的原理是 叶子节点存储数据,非叶子节点存储索引,B+树的每个节点可以存储多个关键字,它将节点大小设置为磁盘页的大小,充分利用了磁盘预读的功能。每次读取磁盘页时就会读取一整个节点,每个叶子节点还有指向前后节点的指针,为的是最大限度的降低磁盘的IO;因为数据在内存中读取耗费的时间是从磁盘的IO读取的百万分之一
而Redis是 内存中读取数据,不涉及IO,因此使用了跳表
skiplist与平衡树、哈希表的比较
- skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。
- 在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。
- 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
- 从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。
- 查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当;而哈希表在保持较低的哈希值冲突概率的前提下,查找时间复杂度接近O(1),性能更高一些。所以我们平常使用的各种Map或dictionary结构,大都是基于哈希表实现的。
- 从算法实现难度上来比较,skiplist比平衡树要简单得多。
redis zset为什么使用skiplist而不用平衡树
- 相对简单易于维护
- 内存消耗相对更低一点
- 链式结构,缓存局部性比平衡树要好很多
- 修改操作,跳表更简单
- 排序后的集合通常是许多ZRANGE或ZREVRANGE操作的目标,即以链表的形式遍历跳跃表。使用这种操作,跳跃表的缓存局部性与其他类型的平衡树一样好。