美文网首页
一 Redis:安装,命令行,事务,乐观锁

一 Redis:安装,命令行,事务,乐观锁

作者: overflowedstack | 来源:发表于2021-04-07 20:23 被阅读0次

    1. 关于Nosql

    1.1 什么是Nosql?

    Not only sql. 不仅仅是sql,泛指非关系型数据库。NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。

    1.2 Nosql为什么会出现?

    互联网高速发展,出现三大问题:高并发读写,对海量数据的高效存储和访问,对数据库高扩展的要求。
    传统关系型数据库难以处理这三大问题。

    • 高并发读写:大量用户同时访问,每秒钟可能会有大量的sql读写操作,关系型数据库硬盘IO难以承受。
    • 海量数据的高效存储和访问:例如某张用户账号表,几亿条数据,在这样的表里进行查询,效率是非常低的。
    • 对数据库高扩展的要求:关系型数据库都有固定的表结构,不太容易扩展。

    而关系型数据库不仅仅难以解决这三大问题,它本身的一些特性在某些互联网场景下,也没有了用武之地。
    例如ACID中的Consistency一致性,可能就用不上了,因为有一些web系统不要求严格的事务一致性。

    1.3 有哪些Nosql数据库?

    • key value 键值对
      redis
    • document 文档数据库
      mongodb, CouchDB
    • 列存储数据库
      HBase,分布式文件系统
    • 图关系数据库
      neo4j

    2. Redis介绍与安装

    2.1 Redis是什么

    REmote DIctionary Server,高性能的key value数据库。

    Redis特性:

    • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
    • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
    • Redis支持数据的备份,即master-slave模式的数据备份。
    • Redis性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
    • Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。

    2.2 Mac安装Redis

    $ brew search redis
    
    $ brew install redis
    
    #启动redis server
    $ redis-server /usr/local/etc/redis.conf
    
    #启一个redis客户端
    $ redis-cli -p 6379 -h 127.0.0.1
    127.0.0.1:6379> ping
    PONG
    

    3. Redis基本使用

    redis默认有16个数据库,在配置文件中有设置:

    $ less /usr/local/etc/redis.conf
    ......
    # Set the number of databases. The default database is DB 0, you can select
    # a different one on a per-connection basis using SELECT <dbid> where
    # dbid is a number between 0 and 'databases'-1
    databases 16
    ......
    

    切换数据库, 查看数据库大小:

    127.0.0.1:6379> select 3   #切换数据库
    OK
    127.0.0.1:6379[3]> dbsize  #查看数据库大小
    (integer) 0
    127.0.0.1:6379[3]> set name test
    OK
    127.0.0.1:6379[3]> dbsize
    (integer) 1
    127.0.0.1:6379[3]> get name
    "test"
    127.0.0.1:6379[3]> select 7
    OK
    127.0.0.1:6379[7]> get name
    (nil)
    127.0.0.1:6379[7]> dbsize
    (integer) 0
    

    清空当前数据库:

    127.0.0.1:6379[3]> keys *  #查看当前数据库所有key
    1) "name"
    127.0.0.1:6379[3]> flushdb  #清空当前数据库
    OK
    127.0.0.1:6379[3]> keys *
    (empty list or set)
    

    清空所有数据库:

    127.0.0.1:6379> keys *
    1) "mylist"
    2) "myset:__rand_int__"
    3) "counter:__rand_int__"
    4) "key:__rand_int__"
    127.0.0.1:6379> select 3
    OK
    127.0.0.1:6379[3]> keys *
    (empty list or set)
    127.0.0.1:6379[3]> flushall   #清空所有数据库
    OK
    127.0.0.1:6379[3]> select 0
    OK
    127.0.0.1:6379> keys *
    (empty list or set)
    

    Redis是单线程的.

    4. Redis支持的数据类型

    redis命令可查阅官方文档 http://redis.cn/commands.html
    五大基本数据类型:string,list,hash,set,zset
    特殊数据类型:geospatial, hyperloglog, bitmap

    4.1 Redis - key

    Exists

    127.0.0.1:6379> set name lilei
    OK
    127.0.0.1:6379> exists name
    (integer) 1
    127.0.0.1:6379> exists name1
    (integer) 0
    

    Keys

    127.0.0.1:6379> keys *
    1) "name"
    

    Expire

    127.0.0.1:6379> expire name 10
    (integer) 1
    127.0.0.1:6379> ttl name
    (integer) 8
    127.0.0.1:6379> ttl name
    (integer) 6
    127.0.0.1:6379> ttl name
    (integer) 1
    127.0.0.1:6379> ttl name
    (integer) -2
    127.0.0.1:6379> get name
    (nil)
    

    Type

    127.0.0.1:6379> set name lilei
    OK
    127.0.0.1:6379> type name
    string
    
    4.2 String

    Append

    127.0.0.1:6379> set name lilei
    OK
    127.0.0.1:6379> append name hello
    (integer) 10
    127.0.0.1:6379> get name
    "lileihello"
    

    Append 不存在则创建

    127.0.0.1:6379> get name1
    (nil)
    127.0.0.1:6379> append name1 hey
    (integer) 3
    127.0.0.1:6379> get name1
    "hey"
    

    Strlen

    127.0.0.1:6379> get name
    "lileihello"
    127.0.0.1:6379> strlen name
    (integer) 10
    127.0.0.1:6379> append name world
    (integer) 15
    127.0.0.1:6379> strlen name
    (integer) 15
    

    incr, decr

    127.0.0.1:6379> set views 0
    OK
    127.0.0.1:6379> get views
    "0"
    127.0.0.1:6379> incr views
    (integer) 1
    127.0.0.1:6379> decr views
    (integer) 0
    127.0.0.1:6379> decr views
    (integer) -1
    

    incrby, decrby

    127.0.0.1:6379> get views
    "-1"
    127.0.0.1:6379> incrby views 10
    (integer) 9
    127.0.0.1:6379> decrby views 5
    (integer) 4
    

    字符串范围 getrange

    127.0.0.1:6379> set key1 "hello,lilei"
    OK
    127.0.0.1:6379> getrange key1 0 5
    "hello,"
    127.0.0.1:6379> getrange key1 0 -1
    "hello,lilei"
    127.0.0.1:6379> getrange key1 -1 0
    ""
    127.0.0.1:6379> getrange key1 -1 -1
    "i"
    127.0.0.1:6379> getrange key1 -2 -1
    "ei"
    

    替换字符串setrange

    127.0.0.1:6379> set key2 helloeveryone
    OK
    127.0.0.1:6379> get key2
    "helloeveryone"
    127.0.0.1:6379> setrange key2 5 all
    (integer) 13
    127.0.0.1:6379> get key2
    "helloallryone"
    

    setex (wet with expire)

    127.0.0.1:6379> setex key3 30 helloworld
    OK
    127.0.0.1:6379> get key3
    "helloworld"
    127.0.0.1:6379> ttl key3
    (integer) -2
    

    setnx (set if not exist) 在分布式锁中常常使用

    127.0.0.1:6379> setnx newkey test
    (integer) 1
    127.0.0.1:6379> get newkey
    "test"
    127.0.0.1:6379> setnx newkey test
    (integer) 0
    127.0.0.1:6379> get newkey
    "test"
    

    mset 批量设置值, mget 批量获取值

    127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3
    OK
    127.0.0.1:6379> keys *
    1) "k3"
    2) "k2"
    3) "k1"
    127.0.0.1:6379> mget k1 k2 k3
    1) "v1"
    2) "v2"
    3) "v3"
    127.0.0.1:6379> mget k2 k3 k1
    1) "v2"
    2) "v3"
    3) "v1"
    

    msetnx 同时设置多个,不存在则创建。原子操作。

    127.0.0.1:6379> msetnx k1 v10 k4 v4
    (integer) 0
    127.0.0.1:6379> keys *
    1) "k3"
    2) "k2"
    3) "k1"
    

    对象

    127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
    OK
    127.0.0.1:6379> mget user:1:name user:1:age
    1) "zhangsan"
    2) "2"
    

    getset组合命令

    127.0.0.1:6379> get k1
    (nil)
    127.0.0.1:6379> getset k1 test
    (nil)
    127.0.0.1:6379> get k1
    "test"
    127.0.0.1:6379>  getset k1 newvalue
    "test"
    127.0.0.1:6379> get k1
    "newvalue"
    

    string类型的应用场景:value除了是字符串还可以是数字,

    • 计数器
    • 统计数量
    4.3 List

    在redis里,可以把list充当栈、队列、阻塞队列。
    所有的list命令都是以 l 开头的。

    lpush 将一个或多个值,放入列表的头部

    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> lrange list 0 0
    1) "three"
    

    rpush 将一个或者多个值,放在列表的尾部:

    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"
    

    lpop rpop 移出列表头部或者尾部的第一个元素:

    127.0.0.1:6379> lrange list 0 -1
    1) "three"
    2) "two"
    3) "one"
    4) "four"
    127.0.0.1:6379> lpop list
    "three"
    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    2) "one"
    3) "four"
    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> lrange list 0 -1
    1) "two"
    2) "one"
    127.0.0.1:6379> lindex list 1
    "one"
    127.0.0.1:6379> lindex list 0
    "two"
    

    llen 获取列表长度:

    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    2) "one"
    127.0.0.1:6379> llen list
    (integer) 2
    

    移除指定个数的指定值:

    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    2) "two"
    3) "one"
    127.0.0.1:6379> lrem list 1 one
    (integer) 1
    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    2) "two"
    127.0.0.1:6379> lrem list 1 two
    (integer) 1
    127.0.0.1:6379> lrange list 0 -1
    1) "two"
    

    ltrim 截取队列的一部分:

    127.0.0.1:6379> lrange mylist 0 -1
    1) "hello"
    2) "hello1"
    3) "hello2"
    4) "hello3"
    127.0.0.1:6379> ltrim mylist 1 2
    OK
    127.0.0.1:6379> lrange mylist 0 -1
    1) "hello1"
    2) "hello2"
    

    rpoplpush 移除列表的尾部元素,并将其放到新列表的头部:

    127.0.0.1:6379> lrange mylist 0 -1
    1) "hello"
    2) "hello1"
    3) "hello2"
    127.0.0.1:6379> rpoplpush mylist myotherlist
    "hello2"
    127.0.0.1:6379> lrange mylist 0 -1
    1) "hello"
    2) "hello1"
    127.0.0.1:6379> lrange myotherlist 0 -1
    1) "hello2"
    

    lset 将列表中指定下标的值更新成另外的值:

    127.0.0.1:6379> exists list
    (integer) 0
    127.0.0.1:6379> lset list 0 item0
    (error) ERR no such key
    127.0.0.1:6379> lpush list value0
    (integer) 1
    127.0.0.1:6379> lrange list 0 0
    1) "value0"
    127.0.0.1:6379> lset list 0 item0
    OK
    127.0.0.1:6379> lrange list 0 0
    1) "item0"
    

    linsert 往某个元素的前后插入一个值:

    127.0.0.1:6379> rpush mylist hello
    (integer) 1
    127.0.0.1:6379> rpush mylist world
    (integer) 2
    127.0.0.1:6379> linsert mylist before world new
    (integer) 3
    127.0.0.1:6379> lrange mylist 0 -1
    1) "hello"
    2) "new"
    3) "world"
    

    消息队列:lpush rpop
    栈:lpush lpop

    4.4 Set 集合

    set中的值不能重复。

    sadd
    smembers
    sismember

    127.0.0.1:6379> sadd myset hello
    (integer) 1
    127.0.0.1:6379> sadd myset world
    (integer) 1
    127.0.0.1:6379> sadd myset everyone
    (integer) 1
    127.0.0.1:6379> smembers myset
    1) "everyone"
    2) "world"
    3) "hello"
    127.0.0.1:6379> sismember myset hello
    (integer) 1
    127.0.0.1:6379> sismember myset hello1
    (integer) 0
    

    scard 获取set中元素个数

    127.0.0.1:6379> smembers myset
    1) "everyone"
    2) "world"
    3) "hello"
    127.0.0.1:6379> scard myset
    (integer) 3
    127.0.0.1:6379> sadd myset everyone
    (integer) 0
    127.0.0.1:6379> sadd myset afternoon
    (integer) 1
    127.0.0.1:6379> scard myset
    (integer) 4
    

    srem 移除集合中的指定元素:

    127.0.0.1:6379> smembers myset
    1) "everyone"
    2) "afternoon"
    3) "world"
    4) "hello"
    127.0.0.1:6379> srem myset afternoon
    (integer) 1
    127.0.0.1:6379> smembers myset
    1) "everyone"
    2) "world"
    3) "hello"
    

    srandmember 随机抽取set中的元素:

    127.0.0.1:6379> srandmember myset
    "world"
    127.0.0.1:6379> smembers myset
    1) "everyone"
    2) "world"
    3) "hello"
    127.0.0.1:6379> srandmember myset
    "everyone"
    

    spop 随机移除集合中元素

    127.0.0.1:6379> smembers myset
    1) "everyone"
    2) "world"
    3) "hello"
    127.0.0.1:6379> spop myset
    "everyone"
    127.0.0.1:6379> smembers myset
    1) "world"
    2) "hello"
    

    将指定值移动到另外一个集合中

    127.0.0.1:6379> smembers myset
    1) "all"
    2) "world"
    3) "hello"
    127.0.0.1:6379> smembers myset2
    1) "set2"
    127.0.0.1:6379> smove myset myset2 all
    (integer) 1
    127.0.0.1:6379> smembers myset
    1) "world"
    2) "hello"
    127.0.0.1:6379> smembers myset2
    1) "all"
    2) "set2"
    

    sdiff 差值
    sinter 交集合,例如微博共同关注
    sunion 并集

    127.0.0.1:6379> smembers key1
    1) "b"
    2) "a"
    3) "c"
    127.0.0.1:6379> smembers key2
    1) "d"
    2) "e"
    3) "c"
    127.0.0.1:6379> sdiff key1 key2
    1) "a"
    2) "b"
    127.0.0.1:6379> sdiff key2 key1
    1) "d"
    2) "e"
    127.0.0.1:6379> sinter key1 key2
    1) "c"
    127.0.0.1:6379> sunion key1 key2
    1) "c"
    2) "b"
    3) "a"
    4) "d"
    5) "e"
    
    4.5 Hash

    Map集合

    hset
    hget
    hmset
    hmget
    hgetall

    127.0.0.1:6379> hset myhash f1 v1
    (integer) 1
    127.0.0.1:6379> hget myhash f1
    "v1"
    127.0.0.1:6379> hmset myhash f1 hello f2 world
    OK
    127.0.0.1:6379> hmget myhash f1 f2
    1) "hello"
    2) "world"
    127.0.0.1:6379> hgetall myhash
    1) "f1"
    2) "hello"
    3) "f2"
    4) "world"
    

    hdel

    127.0.0.1:6379> hgetall myhash
    1) "f1"
    2) "hello"
    3) "f2"
    4) "world"
    127.0.0.1:6379> hdel myhash f1
    (integer) 1
    127.0.0.1:6379> hgetall myhash
    1) "f2"
    2) "world"
    

    hlen

    127.0.0.1:6379> hgetall myhash
    1) "f2"
    2) "world"
    127.0.0.1:6379> hlen myhash
    (integer) 1
    

    hexists

    127.0.0.1:6379> hgetall myhash
    1) "f2"
    2) "world"
    127.0.0.1:6379> hexists myhash f1
    (integer) 0
    127.0.0.1:6379> hexists myhash f2
    (integer) 1
    

    hkeys
    hvals

    127.0.0.1:6379> hgetall myhash
    1) "f2"
    2) "world"
    127.0.0.1:6379> hkeys myhash
    1) "f2"
    127.0.0.1:6379> hvals myhash
    1) "world"
    

    hincrby

    127.0.0.1:6379> hset myhash f3 5
    (integer) 1
    127.0.0.1:6379> hincrby myhash f3 1
    (integer) 6
    

    hsetnx

    127.0.0.1:6379> hsetnx myhash f4 v4
    (integer) 1
    127.0.0.1:6379> hsetnx myhash f4 v5
    (integer) 0
    

    用hash存储变更的数据 user name age,尤其是用户信息。

    4.6 zset 有序集合

    在set的基础上增加了一个值,zset k1 score v1

    127.0.0.1:6379> zadd myzset 100 zhangsan
    (integer) 1
    127.0.0.1:6379> zadd myzset 200 lisi
    (integer) 1
    127.0.0.1:6379> zadd myzset 50 wangwu
    (integer) 1
    127.0.0.1:6379> zrange myzset 0 -1
    1) "wangwu"
    2) "zhangsan"
    3) "lisi"
    127.0.0.1:6379> zadd myzset 150 lilei 80 hanmeimei
    (integer) 2
    127.0.0.1:6379> zrange myzset 0 -1
    1) "wangwu"
    2) "hanmeimei"
    3) "zhangsan"
    4) "lilei"
    5) "lisi"
    

    排序,升序

    127.0.0.1:6379> zrangebyscore myzset -inf +inf
    1) "wangwu"
    2) "hanmeimei"
    3) "zhangsan"
    4) "lilei"
    5) "lisi"
    127.0.0.1:6379> zrangebyscore myzset -inf +inf withscores
     1) "wangwu"
     2) "50"
     3) "hanmeimei"
     4) "80"
     5) "zhangsan"
     6) "100"
     7) "lilei"
     8) "150"
     9) "lisi"
    10) "200"
    127.0.0.1:6379> zrangebyscore myzset -inf 100 withscores
    1) "wangwu"
    2) "50"
    3) "hanmeimei"
    4) "80"
    5) "zhangsan"
    6) "100"
    

    排序,降序

    127.0.0.1:6379> zrevrangebyscore myzset 100 -inf
    1) "zhangsan"
    2) "hanmeimei"
    3) "wangwu"
    127.0.0.1:6379> zrevrangebyscore myzset 100 -inf withscores
    1) "zhangsan"
    2) "100"
    3) "hanmeimei"
    4) "80"
    5) "wangwu"
    6) "50"
    

    zrem, 移除zset中元素

    127.0.0.1:6379> zrange myzset 0 -1
    1) "wangwu"
    2) "hanmeimei"
    3) "zhangsan"
    4) "lilei"
    5) "lisi"
    127.0.0.1:6379> zrem myzset lisi
    (integer) 1
    127.0.0.1:6379> zrange myzset 0 -1
    1) "wangwu"
    2) "hanmeimei"
    3) "zhangsan"
    4) "lilei"
    

    zcard 获取有序集合中的元素个数

    127.0.0.1:6379> zrange myzset 0 -1
    1) "wangwu"
    2) "hanmeimei"
    3) "zhangsan"
    4) "lilei"
    127.0.0.1:6379> zcard myzset
    (integer) 4
    

    zcount 获取指定区间的成员数量

    127.0.0.1:6379> zrangebyscore myzset -inf +inf withscores
    1) "wangwu"
    2) "50"
    3) "hanmeimei"
    4) "80"
    5) "zhangsan"
    6) "100"
    7) "lilei"
    8) "150"
    127.0.0.1:6379> zcount myzset 80 100
    (integer) 2
    127.0.0.1:6379> zcount myzset 80 99
    (integer) 1
    

    zset中支持相同score的不同元素

    127.0.0.1:6379> zrangebyscore myzset -inf +inf withscores
    1) "lily"
    2) "90"
    127.0.0.1:6379> zadd myzset 90 lucy
    (integer) 1
    127.0.0.1:6379> zrangebyscore myzset -inf +inf withscores
    1) "lily"
    2) "90"
    3) "lucy"
    4) "90"
    127.0.0.1:6379> zadd myzset 100 lucy
    (integer) 0
    127.0.0.1:6379> zrangebyscore myzset -inf +inf withscores
    1) "lily"
    2) "90"
    3) "lucy"
    4) "100"
    127.0.0.1:6379> zadd myzset 100 lucy
    (integer) 0
    127.0.0.1:6379> zrangebyscore myzset -inf +inf withscores
    1) "lily"
    2) "90"
    3) "lucy"
    4) "100"
    
    4.7 Geospatial 地理位置

    经度纬度查询 https://jingweidu.bmcx.com

    添加地理位置
    geoadd key longitude latitude member [longitude经度 latitude纬度 member ...]

    127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
    (integer) 1
    127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
    (integer) 1
    127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen
    (integer) 2
    

    获取城市的经度纬度

    127.0.0.1:6379> geopos china:city beijing
    1) 1) "116.39999896287918091"
       2) "39.90000009167092543"
    

    两点之间的距离
    距离单位:m 米,km 千米,mi 英里,ft 英尺

    127.0.0.1:6379> geodist china:city beijing shanghai
    "1067378.7564"
    127.0.0.1:6379> geodist china:city beijing shanghai km
    "1067.3788"
    127.0.0.1:6379> geodist china:city beijing shanghai m
    "1067378.7564"
    127.0.0.1:6379> geodist china:city beijing shanghai mi
    "663.2401"
    127.0.0.1:6379> geodist china:city beijing shanghai ft
    "3501898.8071"
    

    查找我附近的人?
    以当前经度纬度为中心,寻找周围半径多少之内的定位:

    127.0.0.1:6379> georadius china:city 110 30 1000 km
    1) "chongqing"
    2) "shenzhen"
    127.0.0.1:6379> georadius china:city 110 30 800 km
    1) "chongqing"
    
    #显示到中心的距离
    127.0.0.1:6379> georadius china:city 110 30 800 km withdist
    1) 1) "chongqing"
       2) "341.9374"
    
    #显示经度纬度
    127.0.0.1:6379> georadius china:city 110 30 800 km withcoord
    1) 1) "chongqing"
       2) 1) "106.49999767541885376"
          2) "29.52999957900659211"
    
    #显示指定数量的结果
    127.0.0.1:6379> georadius china:city 110 30 1000 km count 1
    1) "chongqing"
    

    找出指定元素周围的其他元素:

    127.0.0.1:6379> georadiusbymember china:city beijing 1000 km
    1) "beijing"
    127.0.0.1:6379> georadiusbymember china:city beijing 2000 km
    1) "chongqing"
    2) "shenzhen"
    3) "shanghai"
    4) "beijing"
    

    geohash 将二维经纬度转换成一维的哈希字符串。

    127.0.0.1:6379> geohash china:city beijing shanghai
    1) "wx4fbxxfke0"
    2) "wtw3sj5zbj0"
    

    GEO底层的实现原理是zset,可以用zset命令来操作geo。

    127.0.0.1:6379> zrange china:city 0 -1
    1) "chongqing"
    2) "shenzhen"
    3) "shanghai"
    4) "beijing"
    127.0.0.1:6379> zrem china:city beijing
    (integer) 1
    127.0.0.1:6379> zrange china:city 0 -1
    1) "chongqing"
    2) "shenzhen"
    3) "shanghai"
    
    4.8 hyperloglog

    hyperloglog是基数统计的算法。
    基数:集合(1,3,5,7,9,7),不重复元素的个数为5,则基数为5.
    例如统计网页的uv(unique visitor, 一个人访问网站多次,还是算作一个人)。
    传统方式,用set保存用户的id,然后就可以统计set中的元素数量作为标准判断。这种方式保存大量的用户id,比较麻烦,耗费内存。
    hyperloglog的优点在于,计算2^64个元素的基数,只需要耗费12kb的内存。不过有0.81%的错误率。hyperloglog的原理涉及到数学和概率学的知识。
    所以如果允许容错,那么可以使用hyperloglog。如果不允许容错,可以使用set,或者自己的数据结构。

    127.0.0.1:6379> pfadd mykey a b c d e f g h i j
    (integer) 1
    127.0.0.1:6379> pfcount mykey
    (integer) 10
    127.0.0.1:6379> pfadd mykey2 i j a b c k l m
    (integer) 1
    127.0.0.1:6379> pfcount mykey2
    (integer) 8
    
    #合并两组元素
    127.0.0.1:6379> pfmerge mykey3 mykey mykey2
    OK
    
    #查看并集的基数
    127.0.0.1:6379> pfcount mykey3
    (integer) 13
    
    4.9 bitmap 位图

    操作二进制位来进行存储,只有0和1两个状态。

    适用于统计两个状态的信息,例如用户打卡信息。

    #例如记录周一到周日打卡信息
    127.0.0.1:6379> setbit sign 0 0
    (integer) 0
    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 1
    (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> getbit sign 3
    (integer) 1
    127.0.0.1:6379> getbit sign 6
    (integer) 0
    
    #统计打卡记录,一共打卡5天
    127.0.0.1:6379> bitcount sign
    (integer) 5
    

    5. Redis事务

    5.1 Redis事务特点

    Redis事务没有隔离级别的概念。
    Redis单条命令保证原子性,但是事务不保证原子性。

    Redis事务:一组命令的集合。

    5.2 正常执行事务
    • 开启事务 (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 k2
    QUEUED
    127.0.0.1:6379> set k3 v3
    QUEUED
    127.0.0.1:6379> exec
    1) OK
    2) OK
    3) "v2"
    4) OK
    
    5.3 取消事务
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> set k1 v10
    QUEUED
    127.0.0.1:6379> set k2 v20
    QUEUED
    127.0.0.1:6379> get k1
    QUEUED
    127.0.0.1:6379> set k4 v4
    QUEUED
    127.0.0.1:6379> discard
    OK
    127.0.0.1:6379> get k4
    (nil)
    
    5.4 编译型异常(代码有问题,命令有错误),那么所有命令都不会被执行。
    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> getset k2
    (error) ERR wrong number of arguments for 'getset' command
    127.0.0.1:6379> set k3
    (error) ERR wrong number of arguments for 'set' command
    127.0.0.1:6379> set k4 v4
    QUEUED
    127.0.0.1:6379> exec
    (error) EXECABORT Transaction discarded because of previous errors.
    127.0.0.1:6379> get k4
    (nil)
    
    5.5 运行时异常(1/0),那么执行命令的时候,其他命令是可以正常执行的,错误命令会抛出异常。
    127.0.0.1:6379> set k1 v1
    OK
    127.0.0.1:6379> multi
    OK
    127.0.0.1:6379> incr k1
    QUEUED
    127.0.0.1:6379> set k2 v2
    QUEUED
    127.0.0.1:6379> set k3 v3
    QUEUED
    127.0.0.1:6379> get k3
    QUEUED
    
    #虽然第一条命令报错,但是其他命令依然正常执行成功了。
    127.0.0.1:6379> exec
    1) (error) ERR value is not an integer or out of range
    2) OK
    3) OK
    4) "v3"
    127.0.0.1:6379> get k2
    "v2"
    

    6. Redis乐观锁 - 使用watch监控

    乐观锁:每次更新数据的时候,认为不会出问题,所以不会上锁。不过每次更新时,都会比较一下数据。如果数据已经改变了,那么就更新失败。

    使用watch可以充当redis的乐观锁。

    线程1:

    127.0.0.1:6379> get money
    "80"
    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
    
    #在此时,线程2执行,改变了money的值。
    #之后,线程1 exec执行,事务执行失败。因为watch money监测到money的值发生了改变。
    127.0.0.1:6379> exec
    (nil)
    127.0.0.1:6379> get money
    "1000"
    

    线程2:

    127.0.0.1:6379> get money
    "80"
    127.0.0.1:6379> set money 1000
    OK
    

    6. redis配置文件 /usr/local/etc/redis.conf

    6.1 网络

    bind 127.0.0.1 ::1   #绑定的ip
    protected-mode yes   #保护模式
    port 6379  #端口设置
    

    6.2 通用

    daemonize no #以守护进程的方式运行,默认是no
    pidfile /var/run/redis_6379.pid #如果以后台的方式运行,我们就需要指定一个pid文件
    
    #日志
    # Specify the server verbosity level.
    # This can be one of:
    # debug (a lot of information, useful for development/testing)
    # verbose (many rarely useful info, but not a mess like the debug level)
    # notice (moderately verbose, what you want in production probably)
    # warning (only very important / critical messages are logged)
    loglevel notice
    

    6.3 快照

    持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb.aof
    redis是内存数据库,如果没有持久化,那么断电数据就丢失了。

    #如果900s内,至少有1个key进行了修改,就进行持久化操作
    save 900 1
     
    save 300 10
    save 60 10000
    
    stop-writes-on-bgsave-error yes #持久化如果出错,是否继续工作
    
    rdbcompression yes #是否需要压缩rdb文件
    
    dir /usr/local/var/db/redis/ #rdb文件保存路径
    

    6.4 security 安全

    requirepass #设置密码,默认没有设置密码
    

    一般可以通过改配置文件,或者命令行来设置密码。
    下面通过命令行设置密码。

    127.0.0.1:6379> config get requirepass
    1) "requirepass"
    2) ""
    127.0.0.1:6379> config set requirepass "1234567"
    OK
    127.0.0.1:6379> config get requirepass
    (error) NOAUTH Authentication required.
    127.0.0.1:6379> ping
    (error) NOAUTH Authentication required.
    
    #由于设置了密码,所以需要验证密码才能继续操作
    127.0.0.1:6379> auth 1234567
    OK
    127.0.0.1:6379> ping
    PONG
    127.0.0.1:6379> config get requirepass
    1) "requirepass"
    2) "1234567"
    

    6.5 客户端及内存限制

    maxclients 10000 #设置能连接上redis的最大客户端数量
    
    maxmemory <bytes> #redis配置最大的内存数量
    
    maxmemory-policy noeviction #内存到达上限之后的处理策略
      1、volatile-lru:只对设置了过期时间的key进行LRU(默认值) 
      2、allkeys-lru : 删除lru算法的key   
      3、volatile-random:随机删除即将过期key   
      4、allkeys-random:随机删除   
      5、volatile-ttl : 删除即将过期的   
      6、noeviction : 永不过期,返回错误
    

    6.6 APPEND ONLY MODE aof模式

    appendonly no #默认是不开启aof模式的,默认是使用rdb方式持久化,在大部分的情况下,rdb完全够用。
    
    appendfilename "appendonly.aof" #持久化文件的名字
    
    # appendfsync always
    appendfsync everysec #每秒执行一次sync
    # appendfsync no
    

    相关文章

      网友评论

          本文标题:一 Redis:安装,命令行,事务,乐观锁

          本文链接:https://www.haomeiwen.com/subject/fjuyhltx.html