热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Redis基础知识(单线程,类型,缓存)

Redis基础知识(单线程,类型,缓存)1.Redis概念Redis是一个用C语言开发的,开源的高性能非关系型的键值对数据库。Redis可以存储键和不同类型数据结构值之间的映射关系




Redis基础知识(单线程,类型,缓存)

1.Redis概念

Redis是一个用C语言开发的,开源的高性能非关系型键值对数据库。

Redis可以存储 不同类型数据结构值 之间的映射关系。键的类型只能是字符串,而值除了支持最 基础的五种数据类型 外,还支持一些 高级数据类型


基础数据类型:String字符串、List列表、hash字典、set集合,zset有序列表

高级数据类型:bitMap位图、Hyperloglog、布隆过滤器、GeoHash、Pub/Sub、Stream


与传统数据库不同的是 Redis 的数据是 存在内存 中的,所以 读写速度 非常 ,因此 Redis 被广泛应用于 缓存 方向,每秒可以处理超过 10 万次读写操作,是已知性能最快的 Key-Value 数据库。另外,Redis 也经常用来做 分布式锁


2.Redis是单线程的

官方表示,Redis是基于内存操作的,CPU不是Redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了。
redis是c语言写的,官方提供的数据为100000+的QPS(服务器每秒可以查询的最多次数),完全不比Memcache差。


Redis为什么单线程还那么快?


  • 误区:高性能的服务器一定是多线程的?多线程一定比单线程效率高?
  • 速度:CPU > 内存 > 硬盘
  • 核心:

  1. Redis是将所有的数据都放在内存中,所以使用单线程的效率是最高的
  2. 对于多线程,上下文切换是一种十分耗时的操作,对于内存系统来说,单线程效率就是最高的。
  3. 采用多路复用I/O技术可以让单个线程高效的处理多个网络请求。

  • 上下文切换:当前任务在执行完CPU时间片后,切换到另一个任务需要保存自己的状态,以便下次切回这个任务时,可以再加载这个任务的状态,任务从保存到再加载的过程就是一次上下文切换,上下文切换是计算密集型的,需要相当可观的处理器时间,可能是操作系统中时间消耗最大的操作


3.Redis数据类型


3.1 String类型

127.0.0.1:6379> set key value1
OK
127.0.0.1:6379> APPEND key value2 #动态的修改存取的字符串
(integer) 12
127.0.0.1:6379> get key
"value1value2"
127.0.0.1:6379> STRLEN key #当前key的长度
(integer) 12
127.0.0.1:6379> SET views 0
OK
127.0.0.1:6379> INCR views #加1操作,用作浏览量
(integer) 1
127.0.0.1:6379> DECR views #减1操作
(integer) 0
127.0.0.1:6379> INCRBY views 10 #增加指定数字
(integer) 10
127.0.0.1:6379> DECRBY views 5
(integer) 5
127.0.0.1:6379> GETRANGE key 0 3 #字符串的截取[0,3]闭区间
"valu"
#setex (set with expire) 设置过期时间
#setnx (set if not exist) 如果不存在的时候设置
127.0.0.1:6379> SETEX key seconds value
127.0.0.1:6379> SETNX key value
#mset 批量插入键值对
#批量获取mget
#MSETNX 原子性操作,要么一起成功,要么一起失败
127.0.0.1:6379> mset key value [key value ...]
127.0.0.1:6379> MGET key [key ...]
127.0.0.1:6379> MSETNX key value [key value ...]
127.0.0.1:6379> MSET k1 v1 k2 v2
OK
127.0.0.1:6379> MSETNX k1 v1 k4 v4
(integer) 0
127.0.0.1:6379> KEYS *
1) "k1"
2) "k2"
127.0.0.1:6379>
#存取对象的写法
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 20 #推荐写法,拿出不用解析
OK
127.0.0.1:6379> keys *
1) "user:1:age"
2) "user:1:name"
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "20"
127.0.0.1:6379> set user:1 {name:zhangsan,age:20}
OK
127.0.0.1:6379> get user:1
"{name:zhangsan,age:20}"
#组合方法,如果没有该键,返回nil,如果该键曾经有值,返回该键的上一个值
127.0.0.1:6379> getset db redis
(nil)
127.0.0.1:6379> getset db mongodb
"redis"

String类型的使用场景:


  • 计数器
  • 统计多单位的数量
  • 粉丝数
  • 对象缓存存储

3.2 List类型

双端队列,可以进行栈,队列,阻塞队列的操作
所有的list操作都是l开头的

127.0.0.1:6379> LPUSH list one two three #将一个值或者多个值插入列表的头部(左)
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> RPUSH list four ##将一个值或者多个值插入列表的尾部(右侧)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
#移除操作,同样分左pop和右pop
127.0.0.1:6379> LPOP list
"three"
127.0.0.1:6379> RPOP list
"four"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
#通过下标获取lindex
127.0.0.1:6379> LINDEX list 1
"one"
#返回列表的长度
127.0.0.1:6379> LLEN list
(integer) 2
#移除操作 需要输入key 数目(list中的value可以重复,移除从上到下依次移除) 具体的值
127.0.0.1:6379> LREM key count element
#截取操作
127.0.0.1:6379> LRANGE list 0 -1
1) "five"
2) "four"
3) "three"
4) "two"
5) "one"
127.0.0.1:6379> LTRIM list 0 2 #截取范围 [0,2)
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "five"
2) "four"
3) "three"
127.0.0.1:6379>
#移除源列表的最后一个元素并放置在一个新列表中
127.0.0.1:6379> RPOPLPUSH list newList
"three"
127.0.0.1:6379> LRANGE list 0 -1
1) "five"
2) "four"
127.0.0.1:6379> LRANGE newList 0 -1
1) "three"
#lset主要用于list的更新操作
#如果下标存在,更新,如果不存在,报错
127.0.0.1:6379> LSET key index element
#LINSERT 插入操作 在某个值的前面或者后面 插入数据
127.0.0.1:6379> LINSERT key BEFORE|AFTER pivot element
127.0.0.1:6379> LRANGE list 0 -1
1) "4"
2) "four"
127.0.0.1:6379> LINSERT list before four 40
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
1) "4"
2) "40"
3) "four"

List小结:


  • 实际上是一个链表,before Node after,left,right都可以插入值
  • 如果key不存在,创建新的链表
  • 如果key存在,新增内容
  • 如果移除了所有值,会释放该键
  • 在两边插入或者改动值效率最高,中间元素相对来说效率会低一点

3.3 Set类型

set中的值是不能重复的

127.0.0.1:6379> SADD myset set1 set2 set3 #set集合中添加元素
(integer) 3
127.0.0.1:6379> SMEMBERS myset #查看set的所有值
1) "set3"
2) "set2"
3) "set1"
127.0.0.1:6379> SISMEMBER myset set #判断某一个值是否在set集合中
(integer) 0
127.0.0.1:6379> SISMEMBER myset set1
(integer) 1
127.0.0.1:6379> SADD myset set3 #不支持添加重复的值 与java相同
(integer) 0
127.0.0.1:6379> SCARD myset #当前set的数量
(integer) 3
127.0.0.1:6379> SREM myset set2 # 移除set中的元素
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "set3"
2) "set1"
127.0.0.1:6379> SRANDMEMBER myset 1 #从集合中随机抽取
1) "set1"
127.0.0.1:6379> SRANDMEMBER myset 1
1) "set3"
127.0.0.1:6379> SPOP key [count] #随机移除元素
127.0.0.1:6379> SMOVE source destination member #移动元素到另一个集合中
127.0.0.1:6379> SMEMBERS myset
1) "set4"
2) "set3"
3) "set5"
4) "set1"
5) "set6"
127.0.0.1:6379> SMEMBERS myset3
1) "set9"
2) "set6"
3) "set3"
4) "set12"
127.0.0.1:6379> SDIFF myset myset3 #sdiff 查看两个集合中的不同元素
1) "set4"
2) "set1"
3) "set5"
127.0.0.1:6379> SUNION myset myset3 #并集
1) "set4"
2) "set3"
3) "set6"
4) "set1"
5) "set9"
6) "set12"
7) "set5"
127.0.0.1:6379> SINTER myset myset3 #交集
1) "set6"
2) "set3"

3.4 Hash类型

Map集合,key-map的本质和String类型没有太大区别
hash更适用于对象存储,String更适合字符串的存储

#格式
127.0.0.1:6379> hset key field value [field value ...]
127.0.0.1:6379> HGET key field
127.0.0.1:6379> HSET hash name zhangsan age 20 #存数据
(integer) 2
127.0.0.1:6379> hget hash name #取数据
"zhangsan"
127.0.0.1:6379> HMGET hash name age #取多个数据
1) "zhangsan"
2) "20"
127.0.0.1:6379> HGETALL hash #获取所有
1) "name"
2) "zhangsan"
3) "age"
4) "20"
127.0.0.1:6379> HDEL hash age #删除操作
(integer) 1
127.0.0.1:6379> HGETALL hash
1) "name"
127.0.0.1:6379> HLEN hash #获取hash的内容长度
(integer) 1
127.0.0.1:6379> HEXISTS hash age #判断key是否存在
(integer) 0
127.0.0.1:6379> HEXISTS hash name
(integer) 1
127.0.0.1:6379> HVALS hash #只获取hash中的值
1) "zhangsan"
2) "20"
3) "male"
127.0.0.1:6379> HKEYS hash #只获取hash中的键
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> HINCRBY hash age 1 #使用hincrby进行增操作
(integer) 21

3.5 zset类型

在set的基础上增加一个值score,表示权重,通过权重将元素从小到大排序,没有修改操作。内部实现依赖跳跃列表的数据结构。

#示例
127.0.0.1:6379> ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
127.0.0.1:6379> ZADD zset 2500 zhangsan 5000 lisi 2650 wangxiaoer 4300 zhaoxiaosan
(integer) 4
#排序
127.0.0.1:6379> ZRANGE zset 0 -1 #获取值
1) "zhangsan"
2) "wangxiaoer"
3) "zhaoxiaosan"
4) "lisi"
#
127.0.0.1:6379> ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
#根据分值区间遍历zset
127.0.0.1:6379> ZRANGEBYSCORE zset 4000 5000
1) "zhaoxiaosan"
2) "lisi"
#根据分值区间遍历zset,同时遍历分值
127.0.0.1:6379> ZRANGEBYSCORE zset 4000 5000 withscores
1) "zhaoxiaosan"
2) "4300"
3) "lisi"
4) "5000"
#-inf 负无穷 +inf 正无穷
127.0.0.1:6379> ZRANGEBYSCORE zset -inf +inf withscores
#移除操作
127.0.0.1:6379> zrem zset zhaoxiaosan
(integer) 1
#获取zset的元素数量
127.0.0.1:6379> ZCARD zset
(integer) 3
#获取指定区间的人员数量
127.0.0.1:6379> ZCOUNT zset 3000 5000
(integer) 1

zset使用场景:


  • 工资表排序
  • 重要消息的权重判断
  • 排行榜应用实现

3.6 三种特殊的数据类型


3.6.1 Geospatial地理位置

#插入
127.0.0.1:6379> GEOADD key longitude latitude member [longitude latitude member ...]
#例如
#两级无法直接添加,我们一般通过java程序一次性导入数据
127.0.0.1:6379> GEOADD address 116 39 beijing 112 28 hunan 121 31 shanghai
(integer) 3
#获取城市经纬度 获取当前定位,获得的定位一定是个坐标值
127.0.0.1:6379> GEOPOS address beijing hunan
1) 1) "116.00000113248825073"
2) "38.99999918434559731"
2) 1) "112.00000137090682983"
2) "27.99999879696989069"
#返回两个给定位置的距离 表示直线距离
- m 米
- km 千米
- ft 英里
- mi 英尺
127.0.0.1:6379> GEODIST key member1 member2 [m|km|ft|mi]
127.0.0.1:6379> GEODIST address beijing shanghai
"999207.7044"
#GEORADIUS 附近的人的位置 给定经纬度,给定半径
- WITHCOORD 经纬度
- WITHDIST 距离
- WITHHASH 位置元素的 geohash 值
- count 指定数量
127.0.0.1:6379> GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
#某个元素位置的hash值
127.0.0.1:6379> GEOHASH address beijing shanghai
1) "wwfmzesx7y0"
2) "wtw037ms070"
#geo属于zset的扩展
对于geo的获取和移除,使用zrange zrem
127.0.0.1:6379> ZRANGE address 0 -1
1) "hunan"
2) "shanghai"
3) "beijing"

3.6.2 Hyperloglog基数统计


什么是基数?
例如 A{1,3,5,7,8,7}, B{1,3,5,7,8}其基数(不重复的元素)等于5
可以接受一定的误差


Hyperloglog可以用在网站的UV上(一个人访问一个网站多次,但是还是算作一个人)

传统的方式:set 保存用户的id,然后就可以统计set中的元素数量作为标准判断,这个方式如果保存大量的用户id,就会比较麻烦,我们的目的是为了基数,还不是保存用户的id

Hyperloglog的优缺点


  • 优点:占用的内存是固定的,只需要占用12KB的内存
  • 缺点:有一定的误差

3.6.3 bitmap位图

可以用来统计用户信息:活跃、不活跃!登录、未登录!打卡、365打卡!

#从打卡的角度记录
#打卡为1 未打卡为0
127.0.0.1:6379> SETBIT sign 0 1
(integer) 0
127.0.0.1:6379> SETBIT sign 1 0
(integer) 0
127.0.0.1:6379> SETBIT key offset value
#查看某一天是否打卡
127.0.0.1:6379> GETBIT key offset
#统计操作
127.0.0.1:6379> BITCOUNT key [start end]

4.Redis的缓存问题


4.1 为什么要用缓存

日常针对数据库的访问,读的操作要远远大于写的操作,所以需要读的可能性比写大很多。Mysql这类数据库QPS大概是在1w左右,而基于内存的数据库Redis可以达到10w。所以直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的。

因此使用Redis,可以提高数据的访问速度**(高性能),并且极大减小数据库的压力(高并发)**

限于成本原因,一般我们只是使用Redis存储一些常用和主要数据,比如用户登录的信息。

一般而言在使用 Redis 进行存储的时候,我们需要从以下几个方面来考虑:


  • 业务数据常用吗?命中率如何? 如果命中率很低,就没有必要写入缓存;
  • 该业务数据是读操作多,还是写操作多? 如果写操作多,频繁需要写入数据库,也没有必要使用缓存;
  • 业务数据大小如何? 如果要存储几百兆字节的文件,会给缓存带来很大的压力,这样也没有必要;

4.2 使用Redis缓存会出现什么问题

是什么-> 为什么 -> 怎么解决


4.2.1 缓存雪崩问题


  • 是什么同一时间大量缓存数据失效,导致原本访问Redis的请求全部去到数据库,造成数据库短期内CPU和内存压力激增,严重可能导致宕机
  • 为什么:缓存数据设置的过期时间是相同的,并且Redis恰巧将这部分数据全部删除
  • 怎么解决给缓存数据设备过期时间时加一个随机值,大幅度减少缓存在同一个时间过期的情况。

4.2.2 缓存穿透问题


  • 是什么:查询一个不存在于数据库中的数据(如ID为负数),此时缓存一定不存在,所以就会绕过Redis请求数据库,如果查询多了,也会将数据库拉跨。
  • 为什么:请求的数据大量不命中缓存,导致走数据库
  • 怎么解决:1. 使用布隆过滤器提前拦截 2. 把不存在的空对象也放到缓存中,设置一个较短的时间

4.2.3 缓存与数据库双写一致性的问题


  • 是什么:当更新操作发生时,导致的数据库不一致问题
  • 为什么:同时更新Redis和更新数据库的原子性无法得到满足,不论是先更新数据库还是Redis都有可能失效,并且在高并发下还随时伴有顺序未知的读取操作。
  • 怎么解决:利用消息队列来实现消息最终一致性的保证,并且还需要利用消息队列的重试机制来保证能够更新成功,如果多次消费失败,可能是由于网络或redis挂了,需要设置提醒


推荐阅读
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 006_Redis的List数据类型
    1.List类型是一个链表结构的集合,主要功能有push,pop,获取元素等。List类型是一个双端链表的结构,我们可以通过相关操作进行集合的头部或者尾部添加删除元素,List的设 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
  • php yac缓存如何清理,yac和memcache性能对比测试
    yac是Laruence开发的一个php进程共享内存的开源项目。详情可以查看。http:www.laruence.com201303182846.htmlmemcache就不用多说 ... [详细]
author-avatar
小力维2010_622_531
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有