Redis快速入门

一直是写在有道云笔记上的,想转化成博客 发现只能手动复制…


redis配置的一些坑:
redis默认不允许外界访问,需要配置
https://blog.csdn.net/jmkweb/article/details/90553146
https://blog.csdn.net/pingweicheng/article/details/81086466

管理redis
https://www.cnblogs.com/awakenedy/articles/9156086.html
https://blog.csdn.net/q1035331653/article/details/79077260


目前一个基本的互联网项目
在这里插入图片描述启动:redis-server redis.conf
测试性能:redis-benchmark
在这里插入图片描述

基础知识

redis默认有16个数据库,默认使用第0个
使用select [n] 切换

127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> set name tzl
OK
127.0.0.1:6379[3]> dbsize
(integer) 1
127.0.0.1:6379[3]> get name
"tzl"
127.0.0.1:6379[3]> FLUSHALL  //flushdb 
OK
127.0.0.1:6379[3]> DBSIZE
(integer) 0
127.0.0.1:6379> EXPIRE name 10   //设置过期时间
(integer) 1
127.0.0.1:6379> ttl name
(integer) 5
127.0.0.1:6379> type arealist
string

Redis 是单线程的!
Redis是基于内存操作,CPU不是Redis性能瓶颈
为什么Redis单线程还快?

  • 误区1:高性能一定是多线程吗?
  • 误区2:多线程(CPU上下文切换) 一定比单线程效率高
    CPU>内存>硬盘的速度
    核心: Redis是将数据存储在内存中的,所以使用单线程操作效率是最高的,多线程(CPU上下文会切换:耗时),对于内存系统来说,没有上下文切换效率是最高的!多次读写都是在一个CPU上,在内存情况下,是最佳的方案。

Redis数据类型

可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists),集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Luascripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

String(字符串)

GET获取存储在给定键中的值
SET设置存储在给定键中的值
DEL删除存储在给定键中的值(可用于所有类型)
127.0.0.1:6379> set key1 v1
OK
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> APPEND key1 "hello"   //可以追加
(integer) 7
127.0.0.1:6379> get key1
"v1hello"
127.0.0.1:6379> STRLEN key1    //获取字符串长度
(integer) 7
##########################################################
127.0.0.1:6379> set views 0    //初始浏览量0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> INCR views     //自增1
(integer) 1
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> incrby views 10  //指定增量
(integer) 12
127.0.0.1:6379> DECRby views 5   //指定减量
(integer) 7
##########################################################
127.0.0.1:6379> set key1 "npnpnpnp,fighting"
OK
127.0.0.1:6379> get key1
"npnpnpnp,fighting"
127.0.0.1:6379> GETRANGE key1 0 3   //range --> 范围
"npnp"
//替换!
127.0.0.1:6379> set key2 "abcdefg"
OK
127.0.0.1:6379> SETRANGE key2 1 "test" //替换指定位置开始的字符串
(integer) 7
127.0.0.1:6379> get key2
"atestfg"
##########################################################
#setex (set with expire) #设置过期时间
#setnx (set if not exist) #不存在在设置(在分布式锁中会常常使用!)
127.0.0.1:6379> setex key3 30 "hello"   //设置key3 的值为hello,30s过期
OK
127.0.0.1:6379> ttl key3
(integer) 25
127.0.0.1:6379> get key3
"hello"
127.0.0.1:6379> setnx mykey "MongoDB"  //如果mykey 不存在 ,创建mykey
(integer) 1
127.0.0.1:6379> get mykey
"MongoDB"
127.0.0.1:6379> setnx mykey "redis"  //如果mykey存在,创建失败
(integer) 0
127.0.0.1:6379> get mykey
"MongoDB"
##########################################################
#mset
#mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 //同时创建多个值
OK
127.0.0.1:6379> get k3
"v3"
127.0.0.1:6379> mget k1 k2 k3   //同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4  //msetnx 是一个原子性的操作,要么一起失败,要么一起成功
(integer) 0
##########################################################
set user:1{name:zhangsan,age:2} //设置一个user:1对象,值为json字符来保存一个对象
#这里的key是一个巧妙的设计:user:{id}:{filed},如此设计在Redis中是完全ok了
127.0.0.1:6379> mset user:1:name zhangshan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangshan"
2) "2"
##########################################################
getset //先get然后set
127.0.0.1:6379> getset db redis //如果不存在,则返回nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb //如果存在,获取原来的值并设置新值
"redis"
127.0.0.1:6379> get db
"mongodb"

String类似的使用场景 :value除了是字符串还可以是数字

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

List(列表 linked-list)

RPUSH将给定值推入列表的右端
LRANGE获取列表在给定范围上的所有值
LINDEX获取列表在给定位置上的单个元素
LPOP从列表的左端弹出一个值,并返回弹出的值

Redis中,我们可以把list玩出花来,栈、队列、阻塞队列
实际上是一个链表,before node after left right 都可以插入

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
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> RPUSH list testr //将一个值或者多个值插入列表尾部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "testr"
##########################################################
LPOP //从列表的左端弹出一个值并返回它
LINDEX //获取列表在给定位置上的元素
127.0.0.1:6379> Lpop list
"three"
127.0.0.1:6379> Lpop list
"two"
127.0.0.1:6379> LRANGE list 0 -1
1) "one"
2) "testr"
127.0.0.1:6379> lindex list 1
"testr"
127.0.0.1:6379> llen list   //返回长度
(integer) 2
##########################################################
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "two"
3) "one"
4) "testr"
127.0.0.1:6379> Lrem list 2 two          //一处list集合中指定的个数value
(integer) 2
127.0.0.1:6379> LRANGE list 0 -1
1) "one"
2) "testr"
##########################################################
127.0.0.1:6379> Rpush list "hello"
(integer) 1
127.0.0.1:6379> Rpush list "hello1"
(integer) 2
127.0.0.1:6379> Rpush list "hello2"
(integer) 3
127.0.0.1:6379> Rpush list "hello3"
(integer) 4
127.0.0.1:6379> ltrim list 1 2   //通过下标截取指定的长度,这个list被改变
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "hello1"
2) "hello2"
##########################################################
rpoplpush //移除列表的最后一个元素,将它移动到新的列表中
lset //将列表中指定下标的值替换为另外一个值,更新操作
127.0.0.1:6379> rpoplpush list otherlist
"hello2"
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
127.0.0.1:6379> lrange otherlist 0 -1
1) "hello2"
127.0.0.1:6379> lpush list hello1
(integer) 2
127.0.0.1:6379> lrange list 0 -1
1) "hello1"
2) "hello1"
127.0.0.1:6379> lset list 0 0
OK
127.0.0.1:6379> lrange list 0 -1
1) "0"
2) "hello1"
##########################################################
127.0.0.1:6379> linsert list  before  "hello1" "other"
(integer) 3
127.0.0.1:6379> lrange list 0 -1
1) "0"
2) "other"
3) "hello1"

Set

与list列表的区别: 都可以同时存储多个字符串,set通过散列表来保证自己存储的每个字符串都是各不相同的

set是无序的,只有键,无键相关联的值(可以想想Java中的HashSet和HashMap)

SADD将给定元素添加到集合
SMEMBERS返回集合中包含的所有元素(元素多的话,速度慢)
SISMEMBER检查给定元素是否存在集合中
SREM如果给定元素存在集合中,那么移除这个元素
127.0.0.1:6379> sadd set-key item              //插入元素成功会返回1,失败为0
(integer) 1
127.0.0.1:6379> sadd set-key item2
(integer) 1
127.0.0.1:6379> sadd set-key item
(integer) 0
127.0.0.1:6379> smembers set-key
1) "item3"
2) "item2"
3) "item"
127.0.0.1:6379> sismember set-key item4      //判断是否有这个值,无返回0 有返回1
(integer) 0
127.0.0.1:6379> sismember set-key item
(integer) 1
127.0.0.1:6379> srem set-key item2          //删除值
(integer) 1
127.0.0.1:6379> srem set-key item2
(integer) 0
127.0.0.1:6379> smembers set-key
1) "item3"
2) "item"
##########################################################
127.0.0.1:6379> SRANDMEMBER set-key 1     //随机抽选出指定个数的元素
1) "item"
127.0.0.1:6379> SRANDMEMBER set-key 1
1) "item3"
##########################################################
微博,B站,共同关注(并集)
127.0.0.1:6379> sadd set-key b
(integer) 1
127.0.0.1:6379> sadd set-key c
(integer) 1
127.0.0.1:6379> sadd set-key d
(integer) 1
127.0.0.1:6379> sadd set-key e
(integer) 1
127.0.0.1:6379> sadd set-key1 c
(integer) 1
127.0.0.1:6379> sadd set-key1 d
(integer) 1
127.0.0.1:6379> sadd set-key1 e
(integer) 1
数字集合类
- 差集
127.0.0.1:6379> SDIFF set-key set-key1
1) "b"
2) "a"
- 并集
127.0.0.1:6379> SUNION set-key set-key1
1) "a"
2) "b"
3) "d"
4) "e"
5) "c"
- 交集
127.0.0.1:6379> SINTER set-key1 set-key
1) "e"
2) "d"
3) "c"

Hash(散列)

map集合
Redis的散列可以存储多个键值对之间的映射(有点像微缩版的Redis) 同样也可以对散列表存储的数字执行自增或自减的操作

HSET在散列里面关联起给定的键值对
HGET获取指定散列键的值
HGETALL获取散列包含的所有键值对
HDEL如果给定键存在于散列里面,那么移出这个键
hash变更的数据user  name age,尤其是用户信息之类的,经常变更的信息!Hash更适合于对象的存储
127.0.0.1:6379> HSET hash-key sub-key1 value       
(integer) 1
127.0.0.1:6379> HSET hash-key sub-key2 value
(integer) 1
127.0.0.1:6379> HGETALL hash-key
1) "sub-key1"
2) "value"
3) "sub-key2"
4) "value"
127.0.0.1:6379> HDEL hash-key sub-key1
(integer) 1
127.0.0.1:6379> HGETALL hash-key
1) "sub-key2"
2) "value"
127.0.0.1:6379> HGET hash-key sub-key2
"value"
##########################################################

sorted set(有序集合)

也是存储键值对的:
有序集合的键被称为成员(memeber) 每个成员都不相同。

集合的值被称为分值(score)必须为浮点数
是Redis中唯一一个既可以根据成员访问元素,又可以根据分值以及分值的排列顺序来访问元素的结构

ZADD将一个带有给定分值的成员添加进集合
ZRANGE根据元素在有序排列中的位置,获取多个元素
ZRANGEBYSCORE获取有序集合在给定分值范围内的所有元素
ZREM移出指定存在的成员
127.0.0.1:6379> ZADD zset-key 728 member1
(integer) 1
127.0.0.1:6379> ZADD zset-key 982 member2
(integer) 1
127.0.0.1:6379> ZRANGE zset-key 0 -1 withscores
1) "member1"
2) "728"
3) "member2"
4) "982"
127.0.0.1:6379> ZRANGEBYSCORE zset-key 600 900 withscores
1) "member1"
2) "728"
127.0.0.1:6379> ZREM zset-key member1
(integer) 1
127.0.0.1:6379> ZRANGE zset-key 0 -1 withscores
1) "member2"
2) "982"
##########################################################

三种特殊类型不复述


事务

Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行
一次性、顺序性、排他性、执行一些列的命令

---------队列 set set set 执行------------

Redis事务没有隔离级别的概念
所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行
Redis单条命令保证原子性的,但是事务不保证原子性
Redis的事务:

  • 开启事务(multi)
  • 命令入队(命令)
  • 执行事务(exec)
  • 开启事务
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set m1 v1
QUEUED
127.0.0.1:6379> set m2 v2
QUEUED
127.0.0.1:6379> get m2
QUEUED
127.0.0.1:6379> get m1
QUEUED
127.0.0.1:6379> set m3 v3
QUEUED
127.0.0.1:6379> exec      //执行事务
1) OK
2) OK
3) "v2"
4) "v1"
5) OK

#####放弃事务

127.0.0.1:6379> multi        //开启事务
OK
127.0.0.1:6379> set m4 v4
QUEUED
127.0.0.1:6379> set m5 v5
QUEUED
127.0.0.1:6379> discard      //取消事务
OK
127.0.0.1:6379> get m4        //事务队列中命令都不会被执行
(nil)

编译异常(代码有问题,命令出错) 事务不会执行
运行时异常(I/O),如果事务队列中存在异常,事务不回停止执行,抛出异常继续执行

监控

悲观锁:

很悲观:认为什么时候都会出问题,无论做什么都会加锁

乐观锁:

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

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

执行失败(在事务过程中,修改了数据)
127.0.0.1:6379> watch money
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
(nil)

在这里插入图片描述

为什么Redis是单线程的也会存在并发问题呢?

虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争。
例子:
有个键,假设名称为myNum,里面保存的是阿拉伯数字,假设现在值为1,存在多个连接对myNum进行操作的情况,这个时候就会有并发的问题。假设有两个连接linkA和linkB,这两个连接都执行下面的操作,取出myNum的值,+1,然后再存回去,看看下面的交互:
linkA get myNum => 1
linkB get myNum => 1
linkA set muNum => 2
linkB set myNum => 2
执行完操作之后,结果可能是2,这和我们预期的3不一致。


Jedis

我们要使用Java来操作Redis

常用API

  • String
  • List
  • Set
  • Hash
  • Zset
public class TestPing {

    public static void main(String[] args) {
        //1.new Jedis 事务
        Jedis jedis = new Jedis("123.56.160.202",6379);
        HashMap<String,String> hashMap = new HashMap<String, String>();
        hashMap.put("1","zxq");
        hashMap.put("2","tzl");
        hashMap.put("3","wll");
        //开启事务
        Transaction multi = jedis.multi();

        try{
            multi.hmset("user",hashMap);

        }catch (Exception e){
            multi.discard();
            e.printStackTrace();
        }finally {
            multi.exec();
            jedis.del("user");
            System.out.println(jedis.hgetAll("user"));
        }

    }

}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值