从零开始学Redis

1、Redis能干什么?

  1. 内存存储、持久化,内存中是断电即失、所以说持久化很重要(rdb、aof)
  2. 效率高,可以用于高速缓存
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器、计数器(浏览量)

2、基础知识

  • redis有16个数据库,默认使用第0个,可以使用select进行切换
127.0.0.1:6379> select 3  #切换数据库
OK
127.0.0.1:6379[3]> dbsize  #查看DB大小
(integer) 0
127.0.0.1:6379> keys *  #查看数据库所有的key
(empty list or set)
127.0.0.1:6379> flushdb  #清空当前数据库
OK
127.0.0.1:6379> flushall  #清空全部数据库的内容
OK
  • Redis是单线程的!

    核心:redis是将所有的数据放在内存中的,所有使用单线程去操作效率就是最高的,多线程(CPU上下文切换:耗时的操作),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上,在内存情况下,这个就是最佳的方案。

3、关于Redis Key的基本命令

127.0.0.1:6379> set name zhangsan  #set key
OK
127.0.0.1:6379> exists name  #判断当前的key是否存在
(integer) 1
127.0.0.1:6379> move name 1  #移除当前数据库的key
(integer) 1
127.0.0.1:6379> expire age 10  #设置key的过期时间,单位是秒
(integer) 1
127.0.0.1:6379> ttl age  #查看当前key的过期时间
(integer) 7 
127.0.0.1:6379> type name  #查看当前key的类型
string

3.1、String(字符串)

127.0.0.1:6379> append name hello  #追加字符串,如果当前key不存在,就相当于set key
(integer) 18
127.0.0.1:6379> strlen name  #获取字符串的长度
(integer) 18
127.0.0.1:6379> incr views  #自增1
(integer) 1
127.0.0.1:6379> get views
"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> getrange key 0 3  #截取字符串 [0,3]
"hell"
127.0.0.1:6379> getrange key 0 -1  #获取全部的字符串
"hello,zhangsan"
127.0.0.1:6379> setex key 30 hello  #设置key的值,并在30s后过期
OK
127.0.0.1:6379> ttl key
(integer) 26
127.0.0.1:6379> setnx key world  #如果key不存在则创建key,存在则创建失败返回0
(integer) 0

3.2、List(列表)

127.0.0.1:6379> lpush list one  #类似一个双端队列,左边是首部,右边是尾部
(integer) 1
127.0.0.1:6379> lpush list two  #从左边插入数据
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1  #通过区间获取list中的值
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> rpush list right  #从右边插入数据
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "three"
2) "two"
3) "one"
4) "right"
127.0.0.1:6379> lpop list  #从左边移除第一个元素
"three"
127.0.0.1:6379> rpop list  #从右边移除第一个元素
"right"
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
127.0.0.1:6379> lindex list 0  #通过下表获得list中的某一个值
"two"
127.0.0.1:6379> llen list  #返回list的长度
(integer) 2
127.0.0.1:6379> lset list 0 hello  #根据下标更新值,如果下标不存在则会报错
OK
127.0.0.1:6379> lrange list 0 -1
1) "hello"
2) "one"
127.0.0.1:6379> linsert list before hello world  #将值插入到某个元素的前面或者后面
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "world"
2) "hello"
3) "one"

可以将List作为:消息队列(Lpush Rpop)、栈(Lpush Lpop)

3.3、Set(集合)

127.0.0.1:6379> sadd myset hello  #往set集合中添加元素
(integer) 1
127.0.0.1:6379> sadd myset world  
(integer) 1
127.0.0.1:6379> smembers myset  #查看set集合中的所有元素
1) "hello"
2) "world"
127.0.0.1:6379> sismember myset hello  #判断某个值是否在set集合中
(integer) 1
127.0.0.1:6379> sismember myset other
(integer) 0
127.0.0.1:6379> scard myset   #判断某个set中元素的个数
(integer) 2
127.0.0.1:6379> srem myset hello  #移除set中某个元素
(integer) 1
127.0.0.1:6379> sdiff key1 key2  #两个集合的差集
1) "a"
127.0.0.1:6379> sinter key1 key2  #交集
1) "c"
2) "b"
127.0.0.1:6379> sunion key1 key2  #并集
1) "b"
2) "a"
3) "e"
4) "c"

3.4、Hash(哈希)

Map集合,key–map,值为一个map集合

127.0.0.1:6379> hset myhash 1 hello  #存入一个map(key--value)
(integer) 1
127.0.0.1:6379> hset myhash 2 world
(integer) 1
127.0.0.1:6379> hget myhash 1   #根据key取出value
"hello"
127.0.0.1:6379> hmget myhash 1 2  #获取多个字段值
1) "hello"
2) "world"
127.0.0.1:6379> hgetall myhash  #获取全部的数据
1) "1"
2) "hello"
3) "2"
4) "world"
127.0.0.1:6379> hdel myhash 1  #根据key删除指定的key
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "2"
2) "world"
127.0.0.1:6379> hlen myhash  #查看hash表的字段数量
(integer) 1
127.0.0.1:6379> hexists myhash hello  #判断hash中指定字段是否存在
(integer) 0
127.0.0.1:6379> hkeys myhash    #获取所有的key
1) "2"
127.0.0.1:6379> hvals myhash    #获取所有的value
1) "world"
127.0.0.1:6379> hset myhash hello 1
(integer) 1
127.0.0.1:6379> hincrby myhash hello 2   #根据增量增加
(integer) 3
127.0.0.1:6379> hincrby myhash hello -4
(integer) -1

3.5、Zset(有序集合)

在set的基础上,增加了一个数值用来将元素进行排序

127.0.0.1:6379> zadd myset 1 hello  #添加元素
(integer) 1
127.0.0.1:6379> zadd myset 2 world
(integer) 1
127.0.0.1:6379> zrange myset 0 -1  #查看所有的元素,默认从小到大
1) "hello"
2) "world"
127.0.0.1:6379> zrange myset 0 -1 withscores    #查看所有元素及分数
1) "hello"
2) "1"
3) "world"
4) "2"
127.0.0.1:6379> zrem myset hello  #移除元素
(integer) 1
127.0.0.1:6379> zcard myset   #获取有序集合中的个数
(integer) 1
127.0.0.1:6379> zcount myset 0 11   #获取指定区间的成员数量
(integer) 2

案列思路:set排序 存储班级成绩单 工资单排序

普通消息 1,重要消息 2;带权重进行判断

4、三种特殊数据类型

4.1、geospatial地理位置

朋友的定位,附近的人,打车的距离计算?

Geo可以推算地理位置的信息,两地之间的距离,方圆几里的人。

可以查询一些测试数据:http://www.jsons.cn/lngcode/

127.0.0.1:6379> geoadd china:city 106.504962 29.533155 chongqi
(integer) 1   #添加地理位置(经度 纬度 名称)
127.0.0.1:6379> geoadd china:city 116.405285 39.904989 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.472644 31.231706 shanghai
(integer) 1
127.0.0.1:6379> geopos china:city beijin  #获取指定城市的经度和纬度
1) 1) "116.40528291463851929"
   2) "39.9049884229125027"
127.0.0.1:6379> geopos china:city chongqi shanghai
1) 1) "106.50495976209640503"
   2) "29.53315530684997015"
2) 1) "121.47264629602432251"
   2) "31.23170490709807012"
127.0.0.1:6379> geodist china:city chongqi shanghai km  #查看城市之间的直线距离
"1447.3973"
127.0.0.1:6379> georadius china:city 110 30 500 km  #以110,30为中心。寻找方圆500km内的城市
1) "chongqi"
127.0.0.1:6379> georadiusbymember china:city chongqi 1450 km  #找出位于指定元素周围的元素
1) "chongqi"
2) "shanghai"

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。

GEO底层是实现原理其实就是Zset,我们可以使用Zset命令来操作GEO

127.0.0.1:6379> zrange china:city 0 -1  #查看地图中的全部元素
1) "chongqi"
2) "shanghai"
3) "beijing"
127.0.0.1:6379> zrem china:city chongqi  #移除指定元素
(integer) 1

4.2、Hyperloglog

统计不同元素的个数,264数据只需要12KB内存

0.81%的错误率,统计UV任务可以忽略不计

127.0.0.1:6379> pfadd key1 a b c  #创建第一组元素
(integer) 1
127.0.0.1:6379> pfadd key2 1 2 3  #创建第二组元素
(integer) 1
127.0.0.1:6379> pfmerge key key1 key2   #合并key1和key2到可以中去
OK
127.0.0.1:6379> pfcount key   #统计不同元素的个数
(integer) 6

4.3、Bitmap

位运算:统计用户信息,活跃,不活跃;登录,未登录;打卡,未打卡;都是两个状态的,都可以使用Bitmap

使用bitmap来记录:周一到周日的打卡

127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 1
(integer) 0
127.0.0.1:6379> setbit sign 3 0
(integer) 0
127.0.0.1:6379> setbit sign 4 1
(integer) 0
127.0.0.1:6379> setbit sign 5 1
(integer) 0
127.0.0.1:6379> setbit sign 6 0
(integer) 0
127.0.0.1:6379> setbit sign 7 0
(integer) 0

127.0.0.1:6379> getbit sign 4   #查看某一天是否打卡
(integer) 1
127.0.0.1:6379> bitcount sign  #查看这周的打卡记录,也就是1的个数
(integer) 4

5、事务

redis单条命令是保证原子性的,但是事务不保证原子性!也就是一条命令出错,后面命令将继续执行

redis事务没有隔离级别的概念!

所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行!Exec

redis的事务

  • 开启事务(multi)
  • 命令入队
  • 执行事务(exec)
127.0.0.1:6379> multi  #开启事务
OK
127.0.0.1:6379> set k1 v1  #命令入队
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get v2
QUEUED
127.0.0.1:6379> exec  #执行事务
1) OK
2) OK
3) (nil)
127.0.0.1:6379> multi   #开启事务
OK
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> discard   #取消事务
OK
127.0.0.1:6379> get k3   #事务队列中的命令都不会被执行
(nil)

6、乐观锁

悲观锁:

  • 很悲观,认为什么时候都会出问题,无论做什么都会加锁!(影响性能)

乐观锁:

  • 很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据。

6.1、Redis监视测试

正常执行成功:

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money  #监视 money 对象
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec   #事务正常结束,数据期间没有发生变动,这个时候就正常执行成功
1) (integer) 80
2) (integer) 20

测试多线程修改值,使用watch可以当作redis的乐观锁操作:

127.0.0.1:6379> watch money   #监视money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> exec #执行之前,另一个线程修改了money的值,就会导致事务执行失败!
(nil)

如果修改失败,获取最新的值就好

127.0.0.1:6379> unwatch  #如果事务执行失败,就先解锁
OK
127.0.0.1:6379> watch money  #获取最新的值,再次监视,select version
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec   #对比监视的值是否发生了变化,如果没有变化,那么可以执行成功,如果改变就执行失败。
1) (integer) 90
2) (integer) 30

猜你喜欢

转载自blog.csdn.net/qq_42372017/article/details/109270335