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

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  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  #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:安装,命令行,事务,乐观锁)