笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值,找寻数据的秘密,笔者认为,数据的价值不仅仅只体现在企业中,个人也可以体会到数据的魅力,用技术力量探索行为密码,让大数据助跑每一个人,欢迎直筒们关注我的公众号,大家一起讨论数据中的那些有趣的事情。
我的公众号为:livandata
本系统案例的代码在github上:https://github.com/livan123/HBase_note
如有需要可以自行比较运行,本案例整合了redis和hbase,用来练习redis系统,下面为主要的需求和步骤,功能细节可以通过查看源码理解。
1、云笔记需求文档
1)产品背景
之前web应用中本身提供了简单的笔记功能,并且提供了简单的笔记分享,但是由于笔记内容随着时间的推移越来越多,但是老系统使用的架构趋于传统业务处理模型,这样导致系统没有办法应付如此大规模的笔记数据,并且笔记的分享能力有限,也无法做到笔记的随时随地可用于是在原系统中暂时拿掉了这个功能,但是用户对线上记录笔记的需求仍然很强烈,对笔记的要求也远远超过了简单的笔记工具的实现能力,每天建议平台都可以看到用户提出要求开发笔记平台的建议,为了满足用户的要求,我们需要研发一款适合用户使用方式的笔记产品。
2)产品简述
云笔记是在吸收了多年的运营数据,经过精确的大数据分析指导需求模型的建立从而实现的,云笔记通过大数据分布式存储解决方案解决了笔记信息量膨胀的问题,并且通过分布式搜索引擎服务以及数据分析推荐平台的建立提升了用户搜索分享比较的能力,系统可以有针对性的提供用户其他人分享的笔记资源扩充自己的知识行囊,并且在分享之外通过每个人的配额管理实现了非分享笔记的安全私有化,保证了用户的私人空间。通过分布式解决方案用户空间被设计为无限大。业务端同时提供了windows客户端,更加贴近用户的使用习惯,并且数据平台支持移动设备的接入,达到无处不学习,无处不笔记的效果。
3)技术关键词
web平台:
nginx,keepalived+lvs ,tomcat,springmvc,jquery,fckeditor,mybatis
hadoop(hdfs,mapreduce),hbase,zookeeper,thrift,lucene,mysql,redis,
客户端:
.net(winform)
4)功能描述
4.1)笔记本功能
4.1.1)创建笔记本:
点击全部笔记本右侧的“+”号,会出现填写笔记本名字的对话框,填写名字后添加一个笔记本到第一个栏位:
填写笔记名字后,点击创建,笔记本创建成功:
4.1.2)修改笔记本:
双击想要修改的笔记本,修改“笔记本”名字。
4.1.3)删除笔记本:
当“笔记本”下不存在笔记内容时,单击想要删除的笔记本,会出现“X”号。
点击“X”号,会提示是否删除笔记本,点击“是”,笔记本将不进入回收站,直接删除。
如“笔记本”下存在“笔记”,需要将笔记清空后方可删除“笔记本”。
4.2)笔记功能
4.2.1)新建笔记:
点击相应笔记本,会在右侧显示该“笔记本”下的所有笔记,点击相应笔记右侧的“+”号,创建“笔记名字”,填写完毕,点击保存,笔记创建成功。
4.2.2)“新建”或“修改”笔记内容:
点击对应的“笔记”,会在内容栏出现“对应笔记”的内容, “添加”或“修改”笔记内容。
编辑结束,点击保存,笔记保存成功;不点击保存不予保存。
4.2.3)笔记分享:
点击相应笔记,会出现笔记状态,有“分享按钮”时为未公开的笔记。
点击“分享按钮”,会将笔记变为分享状态(分享后的笔记不能取消分享),此时笔记分享成功。
4.2.4)删除笔记:
点击相应笔记,在笔记标题右侧有“X”按钮,点击“X”按钮,会提示“是否删除此笔记?”,点击“是”,笔记进入“回收站”。
4.3)共享笔记查询功能
4.3.1)查询共享笔记:
在搜索共享笔记栏中,输入想要搜索的共享笔记关键字,会在“笔记栏”查询出共享笔记中符合查询条件的所有笔记。
4.3.2)收藏共享笔记:
选中想要收藏的笔记,会在内容栏出现笔记内容,点击内容栏右侧的“收藏”按钮,会弹出“选择收藏笔记本”对话框,选择相应的笔记本,并点击“确定”,笔记收藏成功,笔记收藏后为共享状态,并且可以在本地“编辑”和“保存”。
4.4)回收站
4.4.1)恢复 “笔记”:
点击“回收站”,会在笔记栏列出已经删除的 “笔记”,点击想要恢复的 “笔记”,点击标题右侧的“恢复”按钮,会弹出提示框,提示想要恢复到哪个“笔记本”,选择笔记本,点击回复,即可恢复。
4.4.1)彻底删除 “笔记”:
点击“回收站”,会在笔记栏列出已经删除的 “笔记”,点击想要彻底删除的 “笔记”,点击标题右侧的“垃圾箱”按钮,会弹出提示框,提示确定要彻底删除笔记吗,点击是,笔记彻底删除。
2、云笔记设计
2.1)设计理念
将云笔记信息分别存储在redis和hbase中。
redis(缓存):存储每个用户的笔记本信息
hbase(持久层):存储用户的笔记本信息、笔记本下的笔记列表、笔记具体信息。
2.2)笔记本(notebook)
Redis:类似于一个map
key | Value |
loginName | List<string> |
List<string>: List<笔记本的rowkey | 笔记本名称 | 时间戳 | status>
例子: List<senfeng_134223232343|aaaddd|1401761871307|0>
补充redis的知识:
Redis是一个缓存,所有数据全存储在内存当中,保存(key,value)形式;
如何安装redis:
- 下载redis软件包,将其放在linux服务器上;
- 使用解压命令:tar -zxvf redis-3.0.7.tar.gz
- 解压完成后,进入到redis-3.0.7文件夹中,执行:make,用来编译;
- Make完成后,进入到src文件夹下,执行:make install,用来安装redis;
- 执行完成后,为了方便管理,将Redis文件中的conf配置文件和常用命令移动到统一文件中:
a)在redis-3.0.7下创建文件夹:
mkdir -p/usr/local/redis/bin
mkdir -p/usr/local/redis/ect
b)执行Linux文件移动命令:
mv /lamp/redis-3.0.7/redis.conf /usr/local/redis/etc
cd /lamp/redis-3.0.7/src
mv mkreleasdhdr.sh redis-benchmark redis-check-aof redis-check-dump redis-cli redis-server /usr/local/redis/bin
6. 将redis.conf文件中的daemonize属性改为yes(表明需要在后台运行)
cd etc/
Vim redis.conf
修改对应的内容。
7. 启动redis服务端:.\redis-server /usr/local/redis/etc/redis.conf
8. 启动redis客户端:.\redis-cli;
Redis常用命令:
redis是一种高级的key:value存储系统,其中value支持五种数据类型:
1.字符串(strings)
2.字符串列表(lists)
3.字符串集合(sets)
4.有序字符串集合(sorted sets)
5.哈希(hashes)
而关于key,有几个点要提醒大家:
1.key不要太长,尽量不要超过1024字节,这不仅消耗内存,而且会降低查找的效率;
2.key也不要太短,太短的话,key的可读性会降低;
3.在一个项目中,key最好使用统一的命名模式,例如user:10000:passwd。
具体的操作命令为:
1、在处理字符串类型时:
set mystr "hello world!" //设置字符串类型
get mystr //读取字符串类型
2、在处理list类型时:
redis中的lists在底层实现上并不是数组,而是链表,也就是说对于一个具有上百万个元素的lists来说,在头部和尾部插入一个新元素,其时间复杂度是常数级别的,比如用LPUSH在10个元素的lists头部插入新元素,和在上千万元素的lists头部插入新元素的速度应该是相同的。
虽然lists有这样的优势,但同样有其弊端,那就是,链表型lists的元素定位会比较慢,而数组型lists的元素定位就会快得多。
lists的常用操作包括LPUSH、RPUSH、LRANGE等。我们可以用LPUSH在lists的左侧插入一个新元素,用RPUSH在lists的右侧插入一个新元素,用LRANGE命令从lists中指定一个范围来提取元素。我们来看几个例子:
复制代码代码如下:
//新建一个list叫做mylist,并在列表头部插入元素"1"
127.0.0.1:6379> lpush mylist "1"
//返回当前mylist中的元素个数
(integer) 1
//在mylist右侧插入元素"2"
127.0.0.1:6379> rpush mylist "2"
(integer) 2
//在mylist左侧插入元素"0"
127.0.0.1:6379> lpush mylist "0"
(integer) 3
//列出mylist中从编号0到编号1的元素
127.0.0.1:6379> lrange mylist 0 1
1) "0"
2) "1"
//列出mylist中从编号0到倒数第一个元素
127.0.0.1:6379> lrange mylist 0 -1
1) "0"
2) "1"
3) "2"
lists的应用相当广泛,随便举几个例子:
1.我们可以利用lists来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样还需要通过ORDER BY来进行排序。
2.利用LRANGE还可以很方便的实现分页的功能。
3.在博客系统中,每片博文的评论也可以存入一个单独的list中。
3、在处理集合类型时:
redis的集合,主要有两种:
一种无序的集合,集合中的元素没有先后顺序:
集合相关的操作也很丰富,如添加新元素、删除已有元素、取交集、取并集、取差集等。我们来看例子:
复制代码代码如下:
//向集合myset中加入一个新元素"one"
127.0.0.1:6379> sadd myset "one"
(integer) 1
127.0.0.1:6379> sadd myset "two"
(integer) 1
//列出集合myset中的所有元素
127.0.0.1:6379> smembers myset
1) "one"
2) "two"
//判断元素1是否在集合myset中,返回1表示存在
127.0.0.1:6379> sismember myset "one"
(integer) 1
//判断元素3是否在集合myset中,返回0表示不存在
127.0.0.1:6379> sismember myset "three"
(integer) 0
//新建一个新的集合yourset
127.0.0.1:6379> sadd yourset "1"
(integer) 1
127.0.0.1:6379> sadd yourset "2"
(integer) 1
127.0.0.1:6379> smembers yourset
1) "1"
2) "2"
//对两个集合求并集
127.0.0.1:6379> sunion myset yourset
1) "1"
2) "one"
3) "2"
4) "two"
对于集合的使用,也有一些常见的方式,比如,QQ有一个社交功能叫做“好友标签”,大家可以给你的好友贴标签,比如“大美女”、“土豪”、“欧巴”等等,这时就可以使用redis的集合来实现,把每一个用户的标签都存储在一个集合之中。
一种有序的集合,集合中的元素有先后顺序:
redis不但提供了无需集合(sets),还很体贴的提供了有序集合(sorted sets)。有序集合中的每个元素都关联一个序号(score),这便是排序的依据。
很多时候,我们都将redis中的有序集合叫做zsets,这是因为在redis中,有序集合相关的操作指令都是以z开头的,比如zrange、zadd、zrevrange、zrangebyscore等等
老规矩,我们来看几个生动的例子:
//新增一个有序集合myzset,并加入一个元素baidu.com,给它赋予的序号是1:
复制代码代码如下:
127.0.0.1:6379> zadd myzset 1 baidu.com
(integer) 1
//向myzset中新增一个元素360.com,赋予它的序号是3
127.0.0.1:6379> zadd myzset 3 360.com
(integer) 1
//向myzset中新增一个元素google.com,赋予它的序号是2
127.0.0.1:6379> zadd myzset 2 google.com
(integer) 1
//列出myzset的所有元素,同时列出其序号,可以看出myzset已经是有序的了。
127.0.0.1:6379> zrange myzset 0 -1 with scores
1) "baidu.com"
2) "1"
3) "google.com"
4) "2"
5) "360.com"
6) "3"
//只列出myzset的元素
127.0.0.1:6379> zrange myzset 0 -1
1) "baidu.com"
2) "google.com"
3) "360.com"
4、在处理hash类型时:
hashes存的是字符串和字符串值之间的映射,比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。
我们来看一个例子:
复制代码代码如下:
//建立哈希,并赋值
127.0.0.1:6379> HMSET user:001 username antirez password P1pp0 age 34
OK
//列出哈希的内容
127.0.0.1:6379> HGETALL user:001
1) "username"
2) "antirez"
3) "password"
4) "P1pp0"
5) "age"
6) "34"
//更改哈希中的某一个值
127.0.0.1:6379> HSET user:001 password 12345
(integer) 0
//再次列出哈希的内容
127.0.0.1:6379> HGETALL user:001
1) "username"
2) "antirez"
3) "password"
4) "12345"
5) "age"
6) "34"
5、数据的持久化:
redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。
RDB:简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上;
AOF:则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。
6、redis事务处理:
众所周知,事务是指“一个完整的动作,要么全部执行,要么什么也没有做”。
在聊redis事务处理之前,要先和大家介绍四个redis指令,即MULTI、EXEC、DISCARD、WATCH。这四个指令构成了redis事务处理的基础。
1.MULTI用来组装一个事务;
2.EXEC用来执行一个事务;
3.DISCARD用来取消一个事务;
4.WATCH用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行。
纸上得来终觉浅,我们来看一个MULTI和EXEC的例子:
复制代码代码如下:
redis> MULTI //标记事务开始
OK
redis> INCR user_id //多条命令按顺序入队
QUEUED
redis> INCR user_id
QUEUED
redis> INCR user_id
QUEUED
redis> PING
QUEUED
redis> EXEC //执行
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) PONG
在上面的例子中,我们看到了QUEUED的字样,这表示我们在用MULTI组装事务时,每一个命令都会进入到内存队列中缓存起来,如果出现QUEUED则表示我们这个命令成功插入了缓存队列,在将来执行EXEC时,这些被QUEUED的命令都会被组装成一个事务来执行。
对于事务的执行来说,如果redis开启了AOF持久化的话,那么一旦事务被成功执行,事务中的命令就会通过write命令一次性写到磁盘中去,如果在向磁盘中写的过程中恰好出现断电、硬件故障等问题,那么就可能出现只有部分命令进行了AOF持久化,这时AOF文件就会出现不完整的情况,这时,我们可以使用redis-check-aof工具来修复这一问题,这个工具会将AOF文件中不完整的信息移除,确保AOF文件完整可用。
mongoDB:类似于一个list;
hbase:
具体的表存储信息在HBase中进行,表信息为:
表名:nb
rowkey :loginName_ timestamp
列簇1:noteBookInfo(nbi):
列: notebookname(nbn):笔记本名称
createTime(ct):创建时间
status(st):状态
noteList(nl):笔记本下的笔记列表,是个json串(noteRowKey|name| createTime| status)
例如:
| Rowkey | 列簇nbi(notebookinfo) | |||
名称 |
| Nbn(名字) | Ct(创建时间) | St(状态) | Nl(笔记的列表) |
例子 | senfeng403_1321312312 | 学习资料 | 123123123 | 1 | {{“json1”},{“json2”}} |
2.3)笔记(note)
hbase
在笔记本中存在多个笔记,需要将笔记信息存储在表中:
表名:n
RowKey:loginName_timestamp
列簇1:noteInfo(ni):笔记信息
列: notename(nn):笔记的名字;
createTme(ct):创建时间;
status(st):笔记状态;
列簇2:ContentInfo(ci):笔记本容信息
列:content(c):笔记内容
例如:
| Rowkey | 列簇ni | 列簇ci | ||
内容 |
| nn | ct | st | c |
例子 | senfeng_1323242113 | 基础知识 | 1323242113 | 0 | 基础知识内容 |
在设计n表中,存在rowkey字段,这个字段与nb中的笔记列表字段进行关联;
在学习过程中,可以发现,HBase查询比较慢,因此可以将一部分信息放在缓存层,一个人所拥有的笔记数量巨大,但是笔记本数量有限,因此可以将笔记本信息记录在缓存中;需要一个缓存层——redis,用来存储笔记本信息:redis的存储信息为:(key, value),存储结构为:(用户名,list(笔记本信息)),点击笔记本名称,找到对应的笔记本的表,然后再通过笔记本的表找到笔记表的信息。
2.4)创建hbase表语句
create ‘nb’,’nbi’
create ‘n’,’ni’,’ci’
3、功能实现列表
在构建数据时需要将系统开发完成,将其连接到redis、hbase中,才可以使用;
3.1)登陆功能
实现简单的登录验证,需要在登录时查询出该用户下的笔记本,就意味着登录时进入到redis中查一些对应的数据。
3.2)笔记本功能
3.2.1)登陆后查询用户所有笔记
- 在js端,通过页面全局加载方法($(function(){})),调用ajax调用后台,查询用户所有笔记本列表
- 后台通过登录名loginName从redis中查询出笔记本列表信息,返回给前台。如果redis中查不到,在hbase中查询,如果hbase中查询到,恢复redis信息。
- 设置特殊笔记本的rowkey
回收站
rowkey:用户名_0000000000000
收藏夹
rowkey:用户名_0000000000001
活动笔记
rowkey:用户名_0000000000002
4. 初始化判空;
3.2.2)增加笔记本
- 点击增加笔记本按钮,输入笔记本名称,点击确定,ajax发起后台请求
- 后台接受参数为“笔记本名称”,生成信息,存到redis和hbase中。此处会产生事务,事务处理机制需要自己设计。
- 事务:hbase存储成功,此事务成功,hbase存储失败,删除redis中的内容,返回失败。
3.2.3)修改笔记本名称
- 双击笔记本名称,弹出修改笔记本对话框,重新输入名称,点击确定,发起ajax请求
- 后台接受参数,修改redis和hbase信息
- 事务同上
3.2.4)删除笔记本
- 点击删除按钮,确定删除,发起ajax请求
- 后台查询笔记本中是否包含笔记信息,如果包含,提示删除先笔记,如果不包含,删除redis和hbase中的笔记本即可
- 事务同上
3.3)笔记功能
3.3.1)查询笔记本下的所有笔记
- 点击笔记本时,查询笔记本下的所有笔记
- 通过笔记本rowKey到redis中查询笔记列表,如果redis查询不到,从hbase中查询,恢复redis。
- 初始化判空
3.3.2)新建笔记
- 点击新建笔记按钮,输入笔记名称,点击确定,发起ajax请求
- 后台接收参数“笔记名称”,生成信息,存到redis和hbase
- 事务同上
3.3.3)查询笔记内容
- 点击笔记,发起ajax请求信息,查询笔记
- 后台接受到笔记的rowKey,从hbase中查询笔记内容,返还页面
- 初始化判空
3.3.4)修改笔记
- 点击笔记后,在右侧显示栏会显示笔记内容,修改内容或者笔记名称后,点击保存按钮,发起ajax请求
- 后台修改redis和hbase内容
- 事务同上
3.3.5)删除笔记
- 点击笔记删除按钮,点击确定,发起ajax请求
- 后台接收参数,当前笔记本rowkey,回收站笔记本rowkey,笔记rowkey,将笔记删除到回收站
- 实际操作为修改redis和hbase中,当前笔记本和回收站的笔记列表
- 事务同上
3.3.6)笔记迁移
- 点击迁移笔记按钮,会弹出笔记迁移到那个笔记本下,点击确定,发起ajax请求
- 后台就收参数:当前笔记本rowkey,迁移到的笔记本rowkey,笔记rowkey
- 过程同删除笔记到回收站
- 事务同上
3.3.7)恢复笔记
- 点击回收站,显示已删除的笔记列表,点击任意笔记,点击恢复,选择恢复到哪个笔记本,点击确定,发起ajax请求
- 后台接受参数:当前笔记本rowkey,恢复到的笔记本rowkey,笔记rowkey
- 恢复过程同上
- 事务同上
3.4)活动
3.4.1)显示活动页面
- 点击右上角活动按钮,显示活动页面
- 活动页面是一个静态的html,页面定期维护,内容全部写死
- 点击页面上的任意一个活动标题,弹出所有参加活动的笔记列表,每个活动相当于一个特殊的笔记本。
3.4.2)参加活动
- 点击参加活动按钮,选择需要参加活动的笔记,发起ajax请求
- 后台接受参数:参加活动的笔记本rowkey,参加活动的笔记rowkey,此活动的笔记本rowkey等
- 将参加活动的笔记复制到自己的活动笔记本和要参加的活动的笔记本中一份
- 事务同上
3.5)收藏
3.5.1)收藏笔记
- 在参加活动的笔记本下的笔记列表中,可以点击收藏按钮进行收藏,发起ajax请求
- 后台接受参数:收藏的笔记id
- 将次笔记复制到当前用户的收藏笔记本下。
3.6)特殊笔记本
在初始登陆查询用户所有笔记时,生成特殊笔记本rowkey,返回页面,添加到标签内。
3.6.1)回收站
1、rowkey:用户名_0000000000000
3.6.2)收藏夹
rowkey:用户名_0000000000001
3.6.3)活动笔记
rowkey:用户名_0000000000002
4、代码流程分析
4.1、笔记本操作
4.1.1、创建笔记本
4.1.2、删除笔记本
4.1.3、修改笔记本
4.1.4、查询用户的所有笔记本
4.2、笔记操作
4.2.1、查询笔记本下的所有笔记列表
- 前台传过来的参数:笔记本的rowkey
- 后台处理hbase,具体步骤如下:
- )创建nb表的表链接
- )创建get(笔记本的rowkey)
- )处理result结果集,json
- )将json转换为list
- )处理list中的值,用“|”分割每列,封装到n个note中
- )返回前台
4.2.2、增加笔记
4.2.3、查询笔记详情
- 前台传到后台的参数:笔记的rowkey
- 后台处理:
查询笔记表
4.2.4、修改笔记
4.2.5、迁移笔记
4.2.6、彻底删除笔记