一、数据类型
1、String 字符串。
命令如set 、get等
redis自己实现了一个字符串的数据结构,全名翻译过来是简单动态字符串,简称sds,里面有一个字符指针指向内存开始位置和一个长度属性,其长度分换阈值翻倍增长,类似java ArrayList的实现方式。
2、map 散列
命令如hset、hget等
实现的数据结构就是map或者叫字典也行,hashmap的实现方式跟其它地方的是一样的,hashtable+链桶的方式。
redis中使用map这种数据结构的地方有很多,下面列举几个
1)redis的数据库。redis默认有16个数据库,从0号到15号,但数据库是固定的,不能增加,存储格式如 Map<数据库索引,Map<key,具体的数据类型>>。
2)redis以key value方式存放数据,这也是用map方式存放的
3、list 列表
命令如lpush、rpush、lpop、rpop等
redis中列表中顺序为入表顺序,它的实现方式是双端链表,可以很方便的实现队头队尾的插入或者删除操作,但对于中间的数据操作则会比较耗时,适合在两头操作比较多的业务场景,命令中L和R分别代表左和右。
4、set 集合
命令如sadd、srem等
set 的存储方式会有一点不同,它并不是固定的,如果一开始只有整数,redis会使用intset来保存,随着整数长度的增加,intset会自动升级,当出现了非整数类型的元素的时候,redis会把存储结构转换成hashTable
5、sort set 有序集合
命令如 zadd、zscore等
有序集合和set的主要区别第一个是它有一个分数属性,redis通过这个属性对元素进行排序;第二个是它的存储结构不同,有序集合采用的是跳跃表的方式存储的,即多层链表,查询效率接近二分查找法。
二、事务 。
redis 命令的执行是单线程的,它可以同时接收多个应用程序的命令,把这些命令放到一个管道中,执行线程一个一个的依次执行。这里需要注意的是redis单线程执行不会产生并发问题指的单条命令,多条命令之间应该可能产生并发问题,这时候就需要事务保证原子性。
redis保证原子性有几种方式:
1、使用redis事务命令。redis事务命令以multi开关,exec结束,中间可以有多条命令。redis在接到事务命令的时候不会产即去执行,而是等到exec命令之后把之间的命令同时提交,因为命令执行线程只有一个,那么这一批命令就不会有并发问题。
2、使用lua脚本。redis会将lua脚本当成一个命令,中间不会插入别的命令,因此也能保证原子性。
3、使用set nx px 等语句。有时候我们不需要用事务也能达到我们想要的效果,redis本身就支持一些自带条件的命令,这些命令本身也具有原子性,如果能用这些命令完成需求,比用事务更简单,性能更佳。
三、redis集群
1、主从备份。
这是最基本也最简单的集群,只需要在作为slaver的redis上把slaveof 配置指向作为master的redis地址即可,或者启动命令加上slaveof 参数也一样的。
slaver在启动后会主动向master发送同步命令,master接到同步命令后就全以当前的时间节点为准通过rdb方式(快照)给slaver发送一份全量的数据,之后会定时的通过aof方式(用户操作命令)将用户操作同步到slaver。slaver接到全量数据,就直接替换掉自己原来的数据,然后接收aof方式增量同步数据。
值的注意的是,slaver启动后并不总是会同步全量数据,master会缓存slaver存活状态,当slaver掉线但在一定时间范围内重新连上来后只会增量同步它掉线的这一段时间类增量数据。
主从备份一般用来作读写分离,当读操作远大于写操作的时候备份节点能起到读负载均衡,因为所有主从备份节点数据都是一样的,但注意只能master能写入,否则就会出现集群中数据不一致情况。
2、哨兵模式
哨兵模式解决的问题就是当master挂了,集群自动恢复的问题。
哨兵是一个单独的进程,和redis进程是独立的,它监控master及与这个master相连的每一个slaver,我们配置的时候只需要配置master地址就可以了,它会从master拿到所有slaver的地址。
理论上一个主从备份的集群只需要一个哨兵就行了,但事实并非如此,哨兵本身可能会挂掉,而且可能因为网络原因出现误判(因网络原因master暂时连不上,可能会导致重新选择master,出现脑裂问题)。
为了防止这些问题,最恰当的方式是为每个redis节点都配置一个哨兵,这样哨兵本身也是集群(不需要额外配置哨兵之间就能发现彼此,原因是哨兵连上master后会向一个topic订阅和发送消息通知其它哨兵),同时可以通过配置决定当多少哨兵检测到master挂了才开始选举(这个数量一定要超过半数,防止脑裂发生)。
3、集群
即使是哨兵模式,解决的问题也只是高可用和读写分离,集群性能和容量仍然受限于最小节点,而且redis是一个内存数据库,内存不能无限扩大,这方面的限制更为明显。
所以redis本身支持的集群配置即是为了解决redis横向扩展的问题。
redis集群实现了数据分布式存储,同时每个节点都有备份节点,每个主节点之间形成一张网络,彼此互相监控,如果有一个主节点挂了,它的备份节点将升级为主节点继续提供服务,所以集群是完整的高可用和负载均衡的运行方式。
集群默认使用虚拟槽的方式分配数据,将hash值按段分配到不同的槽,每个节点对应一部分槽。当新增一个节点的时候,槽可以迁移,数据可以重新分配。这种分配方式还是取决于hash值,如果某些关键字比较热点,对应的数据比较多,就可能出现数据倾斜,避免这个问题主要在key的处理上面,比如将key值拆散,加上不同的前缀或者后缀等。
四、持久化
支持两种方式:
1、rdb: 快照方式,备份所有数据。
2、aof: 不备份数据,但记录下用户所有的操作命令,恢复的时候重新执行一下这些命令。
用户可以自已设置要使用哪一种方式为持久化,但在主从节点同步的过程中,不管用户设置的哪一种方式,全量同步都是用的rdb,增量同步都是用的aof,它是不受用户设置影响。
五、发布订阅
和其它消息中间件的发布订阅功能差不多,但需注意的是,发布订阅的消息是不会持久化的,即是说如果发消息的时候没有订阅,那么这条消息就永久的丢了,在一些项目中要求消息不能丢,这种情况是一定不能使用redis的发布订阅来实现的(即使负责人说允许丢部分数据,也需谨慎考虑,说不定上线以后要求就变了),本人走过这样的坑,谨记谨记!!!