1 前言
大家好,我是阿沐!你的收获便是我的喜欢,你的点赞便是对我的认可。
作为一年开发经验的毕业生,在上一个章节跟面试官聊了聊redis的基础数据结构列表类型,我们凭借日常知识积累跟面试官展开了相爱相杀场景以及面试期间内心的活动状况。通过结合项目在实际场景中的运用案例和知识点的细节,稳稳的对答如流。
那么这一章节面试官会考验我们对redis的hash数据结构的原理、场景、注意事项、实战这些点进行考察。
好了,开始我们与面试官的博弈,这将是一个很长很长的面试过程,请大家!
2 数据结构hash的理解
面试官:“小年轻,今天让我考验下你redis的hash数据结构知识,不是很厉害嘛,不给你搞个下马威是不行了,我没面子啊,我不要面子的嘛?”。休息完了,我们就继续下一个话题吧,你是怎么理解哈希类型的呢?
面试者:“嚯嚯嚯,看来是故意来找茬的,讲完sting,讲list,现在hash;告诉你我不怕”。非常非常地自信说道:
redis中的哈希(hash或者散列表),内部存储很多键值对以key - [Field-Value]的形式存储,也是一种数组 链表的二维结构(本身又是一个 键值对结构)。正是因为这样,通常我们可以使用哈希存储一个对象信息。下面是我对hash的关系图如下:
注意点:从上图我们可以看出,哈希的关系隐射实际上是field->value的映射,它们才是一对。
面试官:“不要以为知道一点点概念就洋洋得意,这是作为一个开发最最最基础的理念。”
3 常用的hash指令
面试官:基于上面你对哈希的理解,是否可以简单的介绍下hash的比较常见的指令呢?
面试者:“估计真是看我比较年轻,以为的经验是虚报的,这是要考验我基础是吧!那我可就不客气了”。嗯嗯,那我说说我经常使用的一些操作指令吧。
1、查找select指令操作:
代码语言:javascript复制hget指令:hget key field 获取哈希表key中给定字段的值,不存在返回nil;时间复杂度O(1)。
hgetall指令:hgetall key 获取哈希表key中的所有字段和值,不存在返回空列表;时间复杂度O(n),n是哈希表的大小。
hlen指令:hlen key 获取哈希表key中field的数量,不存在返回0;时间复杂度O(1)。
hmget指令:hmget key [field ...] 获取哈希表key中一个或多个给定字段的值,不存在返回nil;时间复杂度O(n),n为给定字段的数量。
hkeys指令:hkeys key 获取哈希表key中所有字段,不存在返回空表;时间复杂度O(n),n为哈希表的大小。
hscan指令:hscan key cursor(游标) [MATCH pattern(匹配的模式)] [COUNT count(指定从数据集里返回多少元素,默认值为 10 )] 获取哈希表key中匹配元素。
hvals指令:hlen key 获取哈希表key中所有的字段的值,不存在返回空表;时间复杂度O(n),n是哈希表的大小。
hexists指令:hexists key field 获取哈希表key中field是否存在,存在返回1不存在返回0;时间复杂度O(1)。
hstrlen指令:hstrlen key field 获取哈希表key中字段长度,不存在返回0,否则返回长度整数;时间复杂度O(1)。
2、添加insert指令操作:
代码语言:javascript复制hset指令:hset key value 将哈希表key中的字段的值设为value,不存在则创建设置,否则将覆盖旧值;时间复杂度O(1)。
注意点:如果哈希表中字段已经存在且旧值已被新值覆盖,返回0而不是1,不能搞错。
hmset指令:hmset field value [field value ...] 一次将多个field-value数据设置进哈希表中,表中已存在的字段会直接覆盖;时间复杂度O(n),n为field-value的数量。
注意:不同于hset,若哈希表已存在字段值,重复设置将会返回OK,而不是0。
hsetnx指令:hsetnx key field value 仅仅当哈希表中字段不存在时可设置,否则无效;时间复杂度O(1)。
注意:跟setnx不同的是,若设置的字段已存在值,那么当前操作将返回结果集为0而不是OK。
hincrby指令:hincrby key field increment 给哈希表中指定字段增加数值;时间复杂度O(1)。
注意:执行hincrby命令后返回的是字段的最新值,而不是ok或者1。
3、删除delete指令操作:
代码语言:javascript复制hdel指令:hdel key field [field ...] 删除哈希表中一个或多个字段,不存在则忽略;时间复杂度O(n),n为要删除字段的数量。
注意:删除操作返回值是删除成功的数量,不存在的字段将被忽略。
下面是我整理哈希类型命令的时间复杂度,大家可以参考此表:
指令 | 时间复杂度 |
---|---|
hget key field | O(1) |
hgetall key | O(n),n是哈希表的大小 |
hlen key | O(1) |
hmget key [field ...] | O(n),n为给定字段的数量 |
hkeys key | O(n),n为哈希表的大小 |
hexists key field | O(1) |
hstrlen key field | O(1) |
hset key value | O(1) |
hmset field value [field value ...] | O(n),n为field-value的数量 |
hsetnx key field value | O(1) |
hincrby key field increment | O(1) |
hdel key field [field ...] | O(n),n为要删除字段的数量 |
面试官:“咦,年轻人善于整理功能划分呀!可以可以,这样做笔记也是一个不错的选择”。那么我看你简历上你写着熟练掌握redis的应用场景,可以简单说下你是如何在项目中使用哈希数据表嘛?
面试者:“这不是 张飞吃豆芽,小菜一碟”。你好,面试官;没问题的,下面我来阐述我具体的应该场景
3.1 哈希的使用场景
面试者:其实hash的使用在项目中是最常见的一种数据结构,那么我们通常会使用hash结构来存储网站用户的基础信息;也可以用来定时统计指定的某些文章的阅读总数等等。实际上我们都是根据自己的业务场景来决定怎么用。
面试官:嗯嗯,那么可以简单的介绍下你是如何使用的?面试官还是一副严肃的表情,仿佛我欠了她几万块钱一样,搞的这么严肃我都赖的面试了。
3.1.1 用户信息
我们首先创建一个关系型的用户信息数据表,存储用户的基础信息(如果存在冷热数据分离,或者分表分库。做法都一样):
代码语言:javascript复制CREATE TABLE `mumu_user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`user_name` varchar(255) NOT NULL DEFAULT '' COMMENT '用户昵称',
`user_pwd` varchar(64) NOT NULL DEFAULT '' COMMENT '用户密码',
`user_email` varchar(125) NOT NULL DEFAULT '' COMMENT '用户邮箱',
`user_gender` tinyint(2) NOT NULL DEFAULT '0' COMMENT '用户性别 0-保密;1-男;2-女',
`user_desc` varchar(255) NOT NULL DEFAULT '' COMMENT '用户描述',
`create_at` int(10) NOT NULL DEFAULT '0' COMMENT '注册时间',
PRIMARY KEY (`user_id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户信息基础表';
我们可以添加几条用户数据:
代码语言:javascript复制insert into `mumu_user` (`user_name`, `user_pwd`,`user_email`,`user_desc`,`create_at`) VALUES('李阿沐', '123456', '2511221051@qq.com', '我是阿沐', unix_timestamp());
insert into `mumu_user` (`user_name`, `user_pwd`,`user_email`,`user_desc`,`create_at`) VALUES('李阿沐1', '123456789', 'lw1772363381@163.com', '我是阿沐啊', unix_timestamp());
那么我们使用Redis哈希结构存储用户信息的示意图如下:
代码语言:javascript复制<?php
<?php
// 实例化redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
//echo "Server is running: " . $redis->ping();
$data = [
'user_id' => 1001,
'user_name' => '李阿沐',
'user_email' => '2511221051@qq.com',
'user_desc' => '我是阿沐',
];
$key = sprintf('user:info:%u', $data['user_id']);
//向 hash 表中批量添加数据:hMset
$result = $redis->hMSet($key, $data);
$redis->expire($key,120);
if ($result) exit('批量设置用户信息成功!');
exit('批量设置用户信息失败!');
-- 终端查询
127.0.0.1:6379> HGETALL user:info:1001
1) "user_id"
2) "1001"
3) "user_name"
4) "xe6x9dx8exe9x98xbfxe6xb2x90"
5) "user_email"
6) "2511221051@qq.com"
7) "user_desc"
8) "xe6x88x91xe6x98xafxe9x98xbfxe6xb2x90"
面试官:嗯嗯,这是最基础的语法使用场景,没有什么特别强调的,你还可以说说在其他方面的使用吗?
面试者:可以,我就举一个比较简单的案例,通过一个活动中的某一个小部分用hash的一个小场景吧。
3.1.2 抽奖场景
场景:公司要做一个抽奖活动,在网页上共有8个道具可以抽奖,最大的是一辆豪华兰博基尼