专业做数据的网站有哪些,新网站为什么做的这么难,上海专业网站营销,电商网站的二级怎么做文章目录 数据结构特殊的数据结构bitmap 1.string命令1.单值缓存2.对象缓存3.分布式锁4.计数器 2.Hash常用命令应用场景应用场景 4.Set5.Sorted Setzset为什么不用红黑树和用B树 合理的数据编码扩容机制 数据结构
string#xff1a;最基本的数据类型#xff0c;二进制安全的… 文章目录 数据结构特殊的数据结构bitmap 1.string命令1.单值缓存2.对象缓存3.分布式锁4.计数器 2.Hash常用命令应用场景应用场景 4.Set5.Sorted Setzset为什么不用红黑树和用B树 合理的数据编码扩容机制 数据结构
string最基本的数据类型二进制安全的字符串最大512M。 list按照添加顺序保持顺序的字符串列表列表实现队列,元素不唯一先入先出原则 set无序的字符串集合不存在重复的元素 集合各不相同的元素 sorted set有序集合已排序的字符串集合。 hashkey-value对的一种集合hash散列值hash的key必须是唯一的
特殊的数据结构
hyperloglogs、geospatial indexes、bitmaps、streams HyperLogLogs基数统计 GeoRedis3.2 推出地理位置定位用于存储地理位置信息并对存储信息进行操作。 HyperLogLog用来做基数统计算法✁数据结构如统计网站UV。 Bitmaps 用一个比特位来映射某个元素状态在 Redis 中它底层于字符串类型实现可以bitmaps 成作一个以比特位为单位数组
bitmap
介绍bitmap 存储的是连续的二进制数字0 和 1通过 bitmap, 只需要一个 bit 位来表 示某个元素对应的值或者状态key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte所以 bitmap 本身会极大的节省储存空间。使用场景适合需要保存状态信息比如是否签到、是否登录…并需要进一步对这些信息进 行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计比如是否点赞过某个视 频 B.布隆过滤器能判断一定不存在但是不能判断一定存在。 C.双删不能完全解决一致性问题所以缓存数据尽量不要参与逻辑处理。
1.string
字符串string字符串类型是Redis中最为基础的数据存储类型是一个由字节组成的序列他在Redis中是二进制安全的这便意味着该类型可以接受任何格式的数据如JPEG图像数据货Json对象描述信息等是标准的key-value一般来存字符串整数和浮点数。Value最多可以容纳的数据长度为512MB应用场景很常见的场景用于统计网站访问数量当前在线人数等。incr命令(操作) 可以用来做最简单的数据领存可以缆存集个简单的字特串也可以领存某json格式的字符串Reds分布式锁的实现就为/用了这种教结构还包括可以实现计教器Session共享、分布式ID 底层实现 String是最简单的数据类型一般用于复杂的计数功能的缓存微博数粉丝数等。 底层实现方式动态字符串sds 或者 long 1什么是sds sds全称是Simple Dynamic String具有如下显著的特点 ① 可动态扩展内存。sds表示的字符串其内容可以修改也可以追加。 ② 采用预分配冗余空间的方式来减少内存的频繁分配从而优化字符串的增长操作 ③ 二进制安全Binary Safe。sds能存储任意二进制数据而不仅仅是可打印字符。 ④ 与传统的C语言字符串类型兼容。 redis的String 底层数据结构使用sds ① 性能高 ② 内存预分配优化字符串的增长操作 ③ 惰性空间回收优化字符串的缩短操作 string类型底层 sds写的 Redis底层是使用C语言实现的对于字符串类型其做出了改进是一种基于动态字符串sds实现redis作为数据库查询必然多修改也会有一定多sds解决了C语言字符串动态扩展的不方便以及查询长度操作从O(n)变为了O(1)。 sds相比C语言原始字符串最大优势在于空间预分配惰性空间释放性能得到很大提高 C 语言的字符串是 char[]实现的而 Redis 使用 SDSsimple dynamic string 封装 Redis 为什么选择 SDS 结构而 C 语言原生的 char[]不香吗 举例其中一点SDS 中O(1)时间复杂度就可以获取字符串长度而 C 字符串需要遍历整个字符串时间复杂度为 O(n)
命令
自加incr 自减decr 加 incrby 减 decrby 应用场景共享 session、分布式锁计数器、限流
/*** 普通缓存放入并设置时间** param key 键* param value 值* param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期* return true成功 false 失败*/
public boolean setTimeout(String key, Object value, long time) {try {if (time 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}// log.debug(新增redis里的key。key [{}],当前时间为timeDate [{}],设置过期时间为time [{}]秒,GsonUtils.bean2json(key), getNowTimeString(),time);return true;} catch (Exception e) {log.error(根据 key:{}设置缓存数据失败, key, e);return false;}
}1.单值缓存
SET key value GET key 使用上面两条命令可以做用户id存储、商品库存存储等等
2.对象缓存
以缓存user对象为例有以下两种方式 ①SET user:1 value(json格式数据)把对象转json存入redis也是当下常用的方式获取数据需要做数据转换 ②MSET user:1:name zhuge user:1:balance 1888 MGET user:1:name user:1:balance 使用Mset命令把对象拆开存储每一个key只保存对象的一个字段信息适用于经常修改user的某个字段的场景
3.分布式锁
SETNX product:10001 true //操作product:10001 。。。执行业务操作 DEL product:10001 //删除product:10001 其中SETNX key value 命令要求如果key已存在则其他的setnx命令无法对当前key进行操作。 在使用分布式锁时通常还会通过 SET product:10001 true ex 10 nx 命令设置key的超时时间防治死锁
4.计数器
INCR 文章id 可以使用INCR命令实现数量自增可以用于文章阅读量、热度人数统计等用户每点进去一次执行一次INCR命令 5.分布式系统全局序列号 在分布式系统下如果需要分库分表 mysql的数据库自增id已经无法满足分库分表下的id自增这时就需要一个独立于数据库之外的中间件来实现id的分配。 redis的INCR命令可以实现id、序列号的生成但如果用户量非常大每生成一个id、序列号都去redis会给redis添加不小的压力我们可以一次性从redis中自增1000次把序列号放入本地内存中这1000个id用完了再去redis再取1000个可有效降低redis的压力
2.Hash
Hash适合用于存储对象因为一个对象的各个属性正好对应一个hash结构的各个field可以方便地操作对象中的某个字段。 底层数据结构 内部编码ziplist压缩列表 、hashtable哈希表 1底层实现方式压缩列表ziplist 或者 字典dict 哈希表:可以用来存储一些keyvalue对更适合用来存储对象
常用命令
简单使用举例hset key field value、hget key field hset:添加hash数据 hget:获取hash数据
hmget:获取多个hash数据
hset person name bingo
hset person age 20
hset person id 1
hget person name
person {name: bingo,age: 20,id: 1
}/**
* 放入map里面的数据
* param mapValue
* param key
* param value
*/
public void putMap(String mapValue, String key, String value){
redisTemplate.opsForHash().put(mapValue, key, value);
// log.debug(新增redis里的key。key [{}],timeDate [{}],key, getNowTimeString());
}/*** 获取map里面的所有数据* param mapValue*/
public MapString, String getMap(String mapValue){
MapObject, Object data redisTemplate.opsForHash().entries(mapValue);
if(MapUtils.isNotEmpty(data)){MapString, String result new HashMap();data.entrySet().stream().forEach(o - result.put((String)o.getKey(), (String)o.getValue()));return result;
}
return Collections.emptyMap();
}/*** 获取map里面的指定数据* param mapValue*/public String getMapValue(String mapValue, String key){Object value redisTemplate.opsForHash().get(mapValue, key);if(!ObjectUtils.isEmpty(value)){return (String)value;}return null;}/*** 判断map里面的指定数据是否存在* param mapValue*/public Boolean hasMapValue(String mapValue, String key){return redisTemplate.opsForHash().hasKey(mapValue, key);}/*** 删除map里面的数据* param mapValue*/public void delMap(String mapValue, String key){redisTemplate.opsForHash().delete(mapValue, key);
// log.debug(删除redis里的key。key [{}],timeDate [{}],key, getNowTimeString());}MapString,String user new HashMap();
user.put(key1,value1);
user.put(key2,value2);
user.put(key3,value3);
//存入
jedis.hmset(user,user);
//取出user中key1
ListString nameMap jedis.hmget(user,key1);
//删除其中一个键值
jedis.hdel(user,key2);
//是否存在一个键
jedis.exists(user);
//取出所有的Map中的值
IteratorString iter jedis.hkeys(user).iterator();
while(iter.next()){jedis.hmget(user,iter.next());
}应用场景
缓存用户信息 散列hashRedis中的散列可以看成具有String key和String value的map容器可以将多个key-value存储到一个key中。每一个Hash可以存储4294967295个键值对。 应用场景例如存储、读取、修改用户属性nameagepwd等 这个是类似 map 的一种结构这个一般就是可以将结构化的数据比如一个对象前提是这个对象没嵌套其他的对象给缓存在 redis 里然后每次读写缓存的时候可以就操作 hash 里的某个字段。 3.List list 的实现为一个双向链表经常被用作队列使用支持在链表两端进行push和pop操作时间复杂度为O(1)同时也支持在链表中的任意位置的存取操作但是需要对list进行遍历时间复杂度为O(n)。 list 的应用场景非常多比如微博的关注列表粉丝列表消息列表等功能都可以用Redis的 list 结构来实现。可以利用lrange命令做基于redis的分页功能。 1Redis3.2之前的底层实现方式压缩列表ziplist 或者 双向循环链表linkedlist 2Redis3.2及之后的底层实现方式quicklist quicklist是一个双向链表而且是一个基于ziplist的双向链表quicklist的每个节点都是一个ziplist结合了双向链表和ziplist的优点 内部编码ziplist压缩列表、linkedlist链表 我之前用redis list 做促销活动 秒杀 redis list 加lua 嘎嘎乱杀 应用场景 消息队列文章列表 简介列表list类型是用来存储多个有序字符串一个列表最多可以存储 2^32-1 个元素。 简单实用举例 lpush key value [value …] 、lrange key start end 内部编码ziplist压缩列表、linkedlist链表 lpushlpopStack栈 lpushrpopQueue队列 lpshltrimCapped Collection有限集合 lpushbrpopMessage Queue消息队列 list常用命令 简单实用举例 lpush key value [value …] 、lrange key start end lpush:从左边推入 lpop:从右边弹出 rpush从右变推入 rpop:从右边弹出 llen查看某个list数据类型的长度
0开始位置-1结束位置结束位置为-1时表示列表的最后一个位置即查看所有。 lrange mylist 0 -1 比如可以搞个简单的消息队列从 list 头怼进去从 list 尾巴那里弄出来。 lpush mylist 1 lpush mylist 2 lpush mylist 3 4 5
rpop mylist
/*** 获取list全量数据* param key*/
public ListString listAllData(String key){
ListObject range redisTemplate.opsForList().range(key, 0L, -1L);
if(!CollectionUtils.isEmpty(range)){ListString result new ArrayList();range.stream().forEach(o - result.add((String)o));return result;
}
return Collections.emptyList();
}
/*** 把数据放入list* param key* param value*/
public void putList(String key, String value){redisTemplate.opsForList().rightPush(key, value);// log.debug(新增redis里的key。key [{}],timeDate [{}],key, getNowTimeString());
}应用场景
消息队列文章列表 列表listRedis的列表允许用户从序列的两端推入或者弹出元素列表由多个字符串值组成的有序可重复的序列是链表结构。好比Java的linkedList在往两端插入和删除数据时效率是非常高的往中间插入数据效率是很低下的。List中可以包含的最大元素数量是4294967295。 应用场景1.最新消息排行榜。2.消息队列以完成多程序之间的消息交换。可以用push操作将任务存在list中生产者然后线程在用pop操作将任务取出进行执行。消费者 4.集合:和列表类似也可以存储多个元素但是不能重复集合可以进行交售 3.列表:Redis的列表通过命令的组合既可以当做栈也可以当做队列来使用可以用来绩存类似微信公众号、微博等消息流数据并集、差集操作从而可以实现类似我和某人共同关注的人、朋友圈点赞等功能 list 是有序列表这个可以玩儿出很多花样。 比如可以通过 list 存储一些列表型的数据结构类似粉丝列表、文章的评论列表之类的东西。 比如可以通过 lrange 命令读取某个闭区间内的元素可以基于 list 实现分页查询这个是很棒的一个功能基于 redis 实现简单的高性能分页可以做类似微博那种下拉不断分页的东西性能高就一页一页走。
4.Set set是一个存放不重复值的无序集合可做全局去重的功能提供了判断某个元素是否在set集合内的功能这个也是list所不能提供的。基于set可以实现交集、并集、差集的操作计算共同喜好全部的喜好自己独有的喜好等功能。1底层实现方式有序整数集合intset 或者 字典dict 简介集合set类型也是用来保存多个✁字符串元素但是不允许重复元素 简单使用举例sadd key element [element …]、smembers key 内部编码intset整数集合、hashtable哈希表 注意点smembers 和 lrange、hgetall 都属于比较重的命令如果元素过多存 在阻塞Redis的可能性可以使用 sscan 来完成。 set 是无序集合自动去重。 直接基于 set 将系统里需要去重的数据扔进去自动就给去重了如果你需要对一些数据进行快速的全局去重你当然也可以基于 jvm 内存里的 HashSet 进行去重但是如果你的某个系统部署在多台机器上呢得基于 redis 进行全局的 set 去重。 可以基于 set 玩儿交集、并集、差集的操作比如交集吧可以把两个人的粉丝列表整一个交集看看俩人的共同好友是谁对吧。 把两个大 V 的粉丝都放在两个 set 中对两个 set 做交集。 集合setRedis的集合是无序不可重复的和列表一样在执行插入和删除和判断是否存在某元素时效率是很高的。集合最大的优势在于可以进行交集并集差集操作。Set可包含的最大元素数量是4294967295。 应用场景1.利用交集求共同好友。2.利用唯一性可以统计访问网站的所有独立IP。3.好友推荐的时候根据tag求交集大于某个threshold临界值的就可以推荐。
sadd:添加数据 scard:查看set数据中存在的元素个数 sismember:判断set数据中是否存在某个元素 srem:删除某个set数据中的元素 undefined Redis Set的随机元素选取 Redis Set提供了SRANDMEMBER命令可以随机地从Set中选取一个元素。该命令有两种用法 1.1 SRANDMEMBER key从Set中随机选取一个元素不会将该元素从Set中移除。 1.2 SRANDMEMBER key count从Set中随机选取count个不同的元素并以数组的形式返回。如果参数count为负数则会从Set中随机选取|count|个元素但这些元素有可能会重复。
#-------操作一个set-------
#添加元素
sadd mySet 1查看全部元素
smembers mySet判断是否包含某个值
sismember mySet 3删除某个/些元素
srem mySet 1
srem mySet 2 4查看元素个数
scard mySet随机删除一个元素
spop mySet#-------操作多个set-------
#将一个set的元素移动到另外一个set
smove yourSet mySet 2#求两set的交集
sinter yourSet mySet求两set的并集
sunion yourSet mySet
#求在yourSet中而不在mySet中的元素
sdiff yourSet mySet/**
* 把数据放入list
* param key
* param value
*/
public void putSet(String key, String value){
redisTemplate.opsForSet().add(key, value);
// log.debug(新增redis里的key。key [{}],timeDate [{}],key, getNowTimeString());
}/*** 获取set全量数据* param key*/
public SetString getAllSetData(String key){SetObject members redisTemplate.opsForSet().members(key);if(!CollectionUtils.isEmpty(members)){SetString result new HashSet();members.stream().forEach(o - result.add((String)o));return result;}return Collections.emptySet();
}应用场景 用户标签,生成随机数抽奖、社交需求。 自带一个随机获得值 spop myset
5.Sorted Set Sorted set 相比 set 多了一个权重参数score集合中的元素能够按score进行排列。可以做排行榜应用取TOP N操作。另外sorted set可以用来做延时任务。最后一个应用就是可以做范围查找。1底层实现方式ziplist 或者 skiplist 有序集合zset 简介已排序的字符串集合同时元素不能重复 简单格式举例zadd key score member [score member …]zrank key member 底层内部编码ziplist压缩列表、skiplist跳跃表 应用场景排行榜社交需求如用户点赞。 有序集合:集合是无序的有序集合可以设置顺序可以用来头现排行棒功能 有序集合zset和set很像都是字符串的集合都不允许重复的成员出现在一个set中。他们之间差别在于有序集合中每一个成员都会有一个分数(score)与之关联Redis正是通过分数来为集合中的成员进行从小到大的排序。尽管有序集合中的成员必须是卫衣的但是分数(score)却可以重复。 应用场景可以用于一个大型在线游戏的积分排行榜每当玩家的分数发生变化时可以执行zadd更新玩家分数(score)此后在通过zrange获取几分top ten的用户信息。 常用命令
sort set和hash很相似,也是映射形式的存储
zadd:添加
zcard:查询
zrange:数据排序
sorted set 是排序的 set去重但可以排序写进去的时候给一个分数自动根据分数排序。
zadd board 85 zhangsan
zadd board 72 lisi
zadd board 96 wangwu
zadd board 63 zhaoliu获取排名前三的用户默认是升序所以需要 rev 改为降序
zrevrange board 0 3获取某用户的排名
zrank board zhaoliu
/**** param key* param value*/
public void setOnlineSeat(String key, String value) {redisUtils.putZSet(RedisCacheKeyPrefix.INTERFACE_NUM key, value, System.currentTimeMillis()/1000 seatTtl);
}public void removeOnlineSeat(String key, String value) {redisUtils.removeZSet(RedisCacheKeyPrefix.INTERFACE_NUM key, value);
}zset为什么不用红黑树和用B树
zset为什么不用红黑树 插入、删除、查找以及迭代输出有序序列这几个操作红黑树也可以完成且时间复杂度跟跳表一样。但按照区间来查找数据这个操作红黑树的效率没有跳表高 跳表可以做到 O(logn) 的时间复杂度定位区间的起点再在原始链表中顺序往后遍历非常高效。 其他原因还有跳表更容易代码实现跳表更加灵活它可以通过改变索引构建策略有效平衡执行效率和内存消耗。 Tips 跳表不能完全替代红黑树红黑树比跳表的出现要早一些很多编程语言中的 Map 类型都是通过红黑树来实现做业务开发时可以直接拿来用不用费劲自己去实现但跳表没有现成的实现所以在开发中如果想使用跳表必须要自己实现。 zset为什么不用B树 B树的原理是叶子节点存储数据非叶子节点存储索引B树的每个节点可以存储多个关键字它将节点大小设置为磁盘页的大小充分利用了磁盘预读的功能。每次读取磁盘页时就会读取一整个节点,每个叶子节点还有指向前后节点的指针为的是最大限度的降低磁盘的IO;因为数据在内存中读取耗费的时间是从磁盘的IO读取的百万分之一。而Redis是内存中读取数据不涉及IO因此使用了跳表
合理的数据编码
String如果存储数字的话是用 int 类型的编码;如果存储非数字小于等于39 字节的字符串是 embstr大于 39 个字节则是 raw 编码。 List如果列表的元素个数小于 512 个列表每个元素的值都小于 64 字节默认使用 ziplist 编码否则使用 linkedlist 编码 Hash哈希类型元素个数小于 512 个所有值小于 64 字节的话使用ziplist 编码,否则使用 hashtable 编码。 Set如果集合中的元素都是整数且元素个数小于 512 个使用 intset 编码否则使用 hashtable 编码。 Zset当有序集合的元素个数小于 128 个每个元素的值小于 64 字节时使用ziplist 编码否则使用 skiplist跳跃表编码
扩容机制
Redis肯定也有扩容机制因为如果没有扩容的话会导致链表越来越长从而降低查询性能。只不过Redis的扩容机制跟HashMap有点不一样Redis会有2个hashTable第二个table只有再扩容的时候使用当第一个table的容量达到一定量这个量正常是已有的数据是table大小的时候就会扩容但是当有在进行持久化的时候使用量是table容量的5倍的时候扩容 扩容也不会一下子都扩容完成因为一下子把所有的数据从第一个table移到到第二个table耗时太长。所以会采用渐进式rehash分批次的将数据迁移到第二个table。然后把第一个table变量指向新table。第二个table赋空。