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