Redis
NoSQL = Not Only SQL
NewSQL = TiDB
Why MySQL maybe better than NoSQL?
Because support of:
- data consistency
- transaction (ACID)
NoSQL 4 types
Key-value型:Redis,Voldemort,Tokyo Cabinet,Tyrant,BerkeleyDB
优点:快速查询
缺点:数据缺少结构化列存储型:Cassandra,HBase,Riak
用于:分布式文件系统。同一列的数据存在一起。文档型:MongoDB,CouchDB
类似是 a series of key-value pair
常用语web application,数据结构要求不严格。Graph 型 (不需要知道):Neo4J,InfoGrid
常用语social network
Redis
一个单线程的内存数据库,用C写的,支持 100,000 QPS。
底层用了 IO多路复用 (Non-blocking IO 思想)
支持5种类型:
- string
- hash
- list
- set
- sortedset (zset)
目前,Redis 创始人加入了 VMWare 并开源。
Redis use-cases
in-memory database
记录非登录用户的信息。(没必要存入MySQL)
例如游客用户浏览记录,浏览记录。。。Cache
为了提升性能。(注意跟1不同。)
例如:商品数据,广告等。数据过期处理
例如:广告;冷热商品。。。其他 use cases
- 解决 distributed cluster 的session分离问题
- 任务队列(性能 + 线程安全),秒杀
- 分布式lock(也可以用zookeeper)
- 发布订阅消息模式(pub-sub)
- app 排行榜
- 计数器 (eg 网站访问统计)
Use Redis in Java
Jedis, Redisson 是官方推荐的 Java client。
Jedis 开源。只需要添加依赖:
1 |
|
Use It:
1 |
|
Jedis 连接池
直接:Jedis jedis = new Jedis(); 这样是不 thread-safe 的。不行!
正解是 create a pool of connections to Redis , 可复用,而且调用是线程安全的。
这么写:
1 |
|
其他几种type
2. List
原理:doubly linked list,无下标,有序。
1 |
|
3. hashes
k-v只能整取整存(类似 String);
但是hash类型,可以存一个Java object。get()的时候,可以只取一个字段。
例如:取得一个用户(user)的地址(postal address),不需要把整个user信息返回来。
1 |
|
4. Set
1 |
|
5. zset
zset兼具 list 和 set 的特点。
- list:有序,可重复
- set:无序,不可重复
- zset:有序,不可重复
get/set/delete/increment
语法:
1 |
|
数据增减:Incr
语法:
1 |
|
如何保证原子性?
- 通过 transaction
- 通过 Lua 脚本
【重要】:setnx = 当且仅当不存在时,赋值。
eg.
1 |
|
用 SetNX 可以实现分布式lock。
set类型的 spop()可以随机弹出一个值,可以用于公司内部抽奖。
通用性命令
- keys: 查看由哪些key,可以自定义pattern
- del:删掉一个key
- exists:判断key是否存在
- expires:在多少seconds时候,自动过期。(精确到毫秒)
- rename
- type:查看类型,例如string,set,zset,list
建议:Redis用作缓存时,一定要设置expire时间。
(priority越高,时间设置越长,每次都可以重新设置)
Redis as MQ
Redis 也可以用作 消息模式(类似 Kafka,RabbitMQ)
主要区别于:
- 如果消息有重复,或者丢失,处理不如其他MQ。
- 消息middleware中,可靠性,重发,重复消费。
建议用于:
- rate limit 请求限流
两种模式:队列,或者pub-sub。
队列模式
一个生产者,只有一个消费者。
利用 lpush 和 rpop 命令实现。
还有:brpop = blocking right-pop,好处是,如果用rpop会持续失败,相当于每次都建立一个connection,效率太低。
subscribe 模式
一个生产者,有多个消费者。
原理:参考微信群。
命令:
- subscribe:加入群 (消费者)
- publish:群发消息 (生产者)
每次有人(类似群主)发布消息publish,消费者(只要subscribe过)都能收到。
内存和过期策略
一定要设置 maxmemory,默认为0(不设置)。如果超过了max memory,Redis 就崩溃了。
数据淘汰策略
如果数据过期了,自动就清除了。但问题是:即使清理过期后,memory还是不够用怎么办?
共有6种策略:
volatile-lru
从已设置过期时间的数据集(server.db[i].expires)中挑选least-recently-used的数据淘汰volatile-ttl
从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰volatile-random
从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰allkeys-lru
从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰allkeys-random
从数据集(server.db[i].dict)中任意选择数据淘汰no-enviction(驱逐)
默认。不驱逐数据。
评价
volatile-lru 比 volatile-ttl 更好,因为快过期了不代表就没用了。
工业界种,LRU 也比 LFU 更精准
不要用allkey的。
最后:如果所有 ttl的都清理完了,内存还是不够用,会不会清除没过期的数据?
答:不会,Redis直接崩溃。
Redis 事务
skip,好复杂。
基于Redis multi/exec/watch:
1.multi,开启Redis的事务,置客户端为事务态。
2.exec,提交事务,执行从multi到此命令前的命令队列,置客户端为非事务态。
3.discard,取消事务,置客户端为非事务态。
4.watch,监视键值对,作用时如果事务提交exec时发现监视的监视对发生变化,事务将被取消。
悲观锁 Pessimistic Lock
Java synchronized 是悲观锁 (Pessimistic Lock):
- 具有互斥性
- 性能不会太高
- 会使命令串行化(synchronized)。
高并发的场景下,不能用悲观锁。
乐观锁 Optimistic Lock
Redis 事务的最大使用场景:乐观锁 (Optimistic Lock)
基于CAS = compare and swap 的思想。
秒杀经常用 Redis 乐观锁。
Redis 主从
主从复制,类似MySQL那一节课。
如果处理Master宕机:sentinel机制(哨兵)
- client 是连到 Sentinel 上的,这里哨兵相当于一个 proxy。
- 持续 ping 主机,在30s之内没有 response,就重新选举 master
- Election:auto-failover(自动故障迁移):
raft算法(谁先发现,谁是新master)
集群
官方推荐 Redis-cluster 集群方案。
- Redis 3 需要配合Lua脚本。
- Redis 5 直接实现。