# moment_favourite_gradle **Repository Path**: lizebin0918/moment_favourite_gradle ## Basic Information - **Project Name**: moment_favourite_gradle - **Description**: redis实现朋友圈点赞功能 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 8 - **Forks**: 3 - **Created**: 2015-11-26 - **Last Updated**: 2023-05-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 项目名: moment\_favourite\_gradle ## 实现功能如下: * 用户注册及登陆,并把用户`session`存储于`redis`中 * 已登陆用户对朋友圈操作(增删查改),可以对朋友圈(自己或者别人)进行点赞(不能重复点赞),按照时间点赞需要按照时间倒序显示 * 页面可以分页显示朋友圈,并缓存对应结果集 ## 项目采用架构: * 应用层:`gradle + mybatis + Spring + SpringMVC + redis + jsp` * 数据库采用:`MySQL` * 应用服务器:`Tomcat` ## 步骤: * 创建数据库及对应的数据库表 ```sql -- create database: CREATE DATABASE IF NOT EXISTS db_moment_favourite DEFAULT CHARSET utf8 COLLATE utf8_general_ci; -- create table: CREATE TABLE mng_user ( id int AUTO_INCREMENT NOT NULL, name varchar(100) DEFAULTNULL NOT NULL, pwd char(16) NOT NULL, uuid char(32) DEFAULTNULL NOT NULL, KEY (id) ); CREATE UNIQUE INDEX uk_uuid ON mng_user (uuid) USING BTREE; CREATE TABLE mng_moment ( id int AUTO_INCREMENT NOT NULL, content varchar(255) NOT NULL, create_date char(8) NOT NULL, create_time char(6) NOT NULL, uuid char(32) DEFAULTNULL NOT NULL, KEY (id), UNIQUE KEY(uuid) ); ``` * 映射关系采用redis存储: ``` a.用户-朋友圈映射: 数据结构:zset key = `user-moment-mapping:` + userUUID value = [momentUUID...] score = createDate + createTime b.朋友圈-点赞用户id映射: 数据结构:zset(可实现时间倒序) key = `moment-favourite-user-mapping:` + momentUUID value = [userUUID...] score = currentDateTime c.session缓存: 数据结构:hash key = sessionId or userUUID ? value = currentLoginedUser d.结果集缓存: 数据结构:string key = 包名.类名.方法名.[参数] value = json序列化的结果集 e.主页只显示最新的20条朋友圈: 数据结构:list key = theNewestTwentyMoment value = [momentUUID...] f.由于采用队列缓存入库,有可能用户发布朋友圈之后,只是存储到缓存中,并为存储到数据库,所以要在缓存保存朋友圈的完整信息 数据结构:String key = moments:[momentUUID] value = "{\"content\":\"restful's moment 20160111225208547:http-nio-8080-exec-2\",\"createDate\":\"20160111\",\"createTime\":\"225208\",\"uuid\":\"59878af863104adfb14d42ae82366285\"}"( value 为 JSON字符串 ``` * mybatis-generator反向生成实体文件:mybatis-generator-config.xml * 性能测试记录: ##问题 - 提出一个问题,如果突然有大量用户发表朋友圈,并发量大约是1W/S,怎么解决? > - 首先要保证Tomcat能处理高并发,假如tomcat能处理的并发量是1K/S,那就需要集群10个Tomcat? > - 在redis提供双端队列,当用户发表朋友圈,只要把朋友圈信息存到队列Q1 head,则返回“添加成功”。 > - 应用可自定义线程数,对Q1进行轮询,每次获取一个tail实体进行入库,若成功,则继续轮询,若失败,放回Q1 head里。 > - 这里需要考虑队列可靠性问题(断电),可采用AOF持久化。 > - 如果多线程往数据库插入朋友圈,失败了,能想到的原因: > - 不会存在说数据格式有误,因为会有前后端校验,只要能进入队列,就保证该数据的正确性。 > - 是否可以批量入库?既然数据格式都是正确的,批量入库会比单条数据入库效率更高,可以怎么实现? > - 主要问题:系统跟数据库的连接断了,一直连接不上,这样Q1就会一直堆积,这种情况只能依靠监控,或者程序设置峰值;线程就会一直轮询,无尽地消耗着CPU ##压测结果 - Tomcat、MySQL采用默认配置,应用运行在PC上,一共发起12000笔,客户端机器内核数为4 > 4个并发,耗时240秒,得出50笔/秒 > 8个并发,耗时120秒,得出100笔/秒(本地测试:8个并发,耗时30秒,得出400笔/秒) > 16个并发,耗时86秒,得出140笔/秒 > 采用队列缓存“朋友圈”信息,应用相应速度加快,由单条处理变成批处理,性能有一定提升,但是这里会出现一个问题,就是数据在缓存,但是数据库没有。比如刷新首页显示前20条最新的朋友圈,会报错。由于缓存只是存前20条朋友圈的UUID,后端逻辑需要通过UUID到数据库获取整行数据,这时无法获取到,就会报错。