Redis点赞功能的实现

Redis点赞功能

 因为工作,最近需要做点赞功能,刚开始想到是mysql创建两表的做法,后续发现不是不可以,是感觉mysql数据库读写压力大,热门视频会有很多用户点赞,甚至是短时间内被大量点赞, 直接操作数据库从长久来看不是很理想的做法。redis主要的特点就是快, 毕竟主要数据都在内存嘛;(这两句话我抄袭的,干嘛歇着了,方便读者了解性能)

优点

性能高
缓解数据库读写压力
其实我更多的在于缓解写压力, 真的读压力, 通过mysql主从甚至通过加入redis对热点数据做缓存都可以解决,
写压力对于前面的方案确实是不大好使。
另外为啥我选择redis而不是memcache的主要原因在于redis支持更多的数据类型, 例如hash, set, zset等。

缺点

开发复杂
这个比直接写mysql的方案要复杂很多, 需要考虑的地方也很多;
不能保证数据安全性
redis挂掉的时候会丢失数据, 同时不及时同步redis中的数据, 可能会在redis内存置换的时候被淘汰掉;
不过对于我们点赞而已, 稍微丢失一点数据问题不大;
这里就不说mysql的呢,想试试自己去试

Redis的数据类型

因为使用redis,肯定使用到它的数据类型了,为了方便大家快速了解,我把我写的复一下

 Jedis jedis = new Jedis("127.0.0.1", 6379);
        ApplicationContext ctx = new ClassPathXmlApplicationContext("configs/redis.xml");
        redisTemplate = ctx.getBean(RedisTemplate.class);
        //测试数据  String
        String set = jedis.set("xyg", "hello");
        String xyg = jedis.get("xyg");
        System.out.println("===="+xyg);
        //jedis.flushDB();
        System.out.println("===="+xyg);


        /**
         * list
         */
        jedis.lpush("list1","l1");
        jedis.lpush("list1","l2");
        jedis.lpush("list1","l3");
        List<String> lrange = jedis.lrange("list1", 0, jedis.llen("list1"));
        System.out.println("lrange="+lrange);
        Long lrem = jedis.lrem("list1", 0, "l1");
        System.out.println("lrem="+lrem);

        /**
         * hash 结构底层也是 hashMap
         */
        jedis.hset("hash1","h1","v1");
        jedis.hset("hash1","h2","v2");
        jedis.hset("hash1","h3","v3");
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("k3", "v3");
        map.put("k4", "v4");
        map.put("k5", "v5");
        jedis.hmset("hash1", map);

        String hget = jedis.hget("hash1", "h1");
        System.out.println("hget="+hget);
        Long hash1 = jedis.hdel("hash1","k4");
        Map<String, String> hash11 = jedis.hgetAll("hash1");
        System.out.println("hashAll="+hash11);
        /**
         * set
         */
        jedis.sadd("set1","s1");
        jedis.sadd("set1","s2");
        jedis.sadd("set1","s3");
        jedis.sadd("set1","s4");
        jedis.sadd("set1","s2");
        Set<String> set1 = jedis.smembers("set1");
        System.out.println("smembers="+set1);

        Long srem = jedis.srem("set1", "s3");
        Set<String> set11 = jedis.smembers("set1");
        System.out.println("set="+set11);
        //判断对应key的value是否存在
        Boolean sismember = jedis.sismember("set", "s4");

        /**
         * zset
         */
        jedis.zadd("zset1", 60, "v1");
        jedis.zadd("zset1", 70, "v2");
        Map<String, Double> zmap = new HashMap<>();
        zmap.put("v3",80.00);
        zmap.put( "v4",90.00);
        zmap.put("v5",100.00);
        jedis.zadd("zset1",zmap);

        Set<String> zset1 = jedis.zrange("zset1", 0, -1);
        System.out.println("zset1="+zset1);
        Long zrem = jedis.zrem("zset1", "v3");
        System.out.println("数据="+jedis.zrange("zset1",00,-1));
        String set12 = jedis.get("set1");
        System.out.println("==============="+set12);

1 创建jedis连接池

JedisPool jedisPool = null;
//Jedis resource = jedisPool.getResource();
if(null == jedisPool){
synchronized(TestRedis.class){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//控制一个pool可分配多少个jedis实例
jedisPoolConfig.setMaxTotal(100);
//控制一个pool最多有多少个状态为idle(空闲)的jedis实例
jedisPoolConfig.setMaxIdle(32);
//表示当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛JedisConnectionException;
jedisPoolConfig.setMaxWaitMillis(100*1000);
//获得一个jedis实例的时候是否检查连接可用性(ping())
jedisPoolConfig.setTestOnBorrow(true);

            //jedisPool =  new JedisPool("127.0.0.1", 6379);
            //指定连接的db 默认是db0
            jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379, 1000, "root", 4);
        }
    }
    return jedisPool;

`

2 说下一思路

这里说一下,我之前只管了一个集合,点赞的集合,反点的也就是不喜欢的集合我没设置,鉴于有些人也需要,我添加上了。首先判断当前用户是否点赞过,如果没有点赞过,则添加到点赞集合,删除反点集合中的数据如果之前点赞过,则添加到反点的集合并删除点赞集合中的数据,不想绕了,看代码吧

3 使用的Redis的set类型,它是一个无序不重复的集合


 /**
     * 获取某个用户的点赞人数集合
     */
    public Set getValue(String key){
        JedisPool jedisPool = TestRedis.getJedisPoolInstance();
        Jedis jedis = jedisPool.getResource();
        return jedis.smembers(key);
    }
    /**
     * 某个用户value给当前用户点赞
     */
    public void like(String key,String value){
        JedisPool jedisPool = TestRedis.getJedisPoolInstance();
        Jedis jedis = jedisPool.getResource();
        //ShardedJedis jedis = TestRedis.getJedis();
        jedis.sadd(key,value);
    }
    /**
     * 删除某个当前用户的点赞用户
     */
    public void disLike(String key,String value){
        JedisPool jedisPool = TestRedis.getJedisPoolInstance();
        Jedis jedis = jedisPool.getResource();
        jedis.srem(key,value);
    }

    /**
     * 判断某个用户所对应的点赞用户是否存在
     */
    public boolean isTrue(String key,String value){
        JedisPool jedisPool = TestRedis.getJedisPoolInstance();
        Jedis jedis = jedisPool.getResource();
        Boolean sismember = jedis.sismember(key, value);
        return sismember;
    }
    /**
     * 获取当前用户的点赞总数
     */
    public long isSum(String key){
        JedisPool jedisPool = TestRedis.getJedisPoolInstance();
        Jedis jedis = jedisPool.getResource();
        Long scard = jedis.scard(key);
        return scard;
    }

    /**
     * 生成点赞与反对键值:
     *
     *      生成规则:
     *          BIZ_LIKE/BIZ_DISLIKE + SPLIT + xyg + userId(当前登录用户的id,不是点赞人的id)
     *
     *
     */
    private static String SPLIT = ":";
    private static String BIZ_LIKE = "LIKE";
    private static String BIZ_DISLIKE = "DISLIKE";
    private static String likeType = "xyg";
    //UUID.randomUUID().toString().replace("_","").toLowerCase();

    /**
     * 喜欢key
     */
    public String getLikeKey(String userId){
        return BIZ_LIKE+SPLIT+likeType+SPLIT+userId;
    }
    /**
     * 不喜欢key
     */
    public String getDisLikeKey(String userId){
        return BIZ_DISLIKE+SPLIT+likeType+SPLIT+userId;
    }

    /**
     * 测试
     */
    @Test
    public void likeOrDislike(){
        //设置当前用户的userId
        String suerId = "admin";
        /**
         * 前端传过来的点赞人的用户id 如下几个
         *     假设点赞/不点赞人有这介个  abc acb bac bca cab cba
         */

        String userLikeOrDislikeId = "acb";
        //String userLikeOrDislikeId = "acb";
        //首先获取LikeKey
        String likeKey = getLikeKey(suerId);
        String disLikeKey = getDisLikeKey(suerId);
        //判断userLikeOrDislikeId之前是否点赞过
        boolean aTrue = isTrue(likeKey, userLikeOrDislikeId);
        if(!aTrue){
            //如果不存在,即之前没有点赞过,现在存入集合
            like(likeKey,userLikeOrDislikeId);
            disLike(disLikeKey,userLikeOrDislikeId);
        }else{
            //如果存在,则移除改用户,取消点赞userLikeOrDislikeId
            like(disLikeKey,userLikeOrDislikeId);
            disLike(likeKey,userLikeOrDislikeId);
        }
        //最终返回当前用户获取点赞的总数量
        long sum = isSum(likeKey);
        System.out.println("当前用户得到的点赞数量为:"+sum);
    }
}

4 上几张图,这几张图是没有加反点集合的数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5 做完后我有个疑问,我想使用db 1 2 3…其他的库怎么设置,其实代码中已经写好了

百度了好多,换汤不换药,差不多

  //jedisPool =  new JedisPool("127.0.0.1", 6379);
    jedisPool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379, 1000, "root", 4);

这里有两个构造方法,第二个构造方法的 4 就是指的db4库。
如果你是本地的redis,在运行的时候就会出错
ERR Client sent AUTH, but no password is set,反正意思是你的密码不存在,所以呢,设置密码即可
在这里插入图片描述
之后我是添加了反点的集合,使用的 db4 库 ,代码已经在上面了 如下:
看对应的key
在这里插入图片描述
至于ssm 或者 springboot整合redis的,网上找,我这边也把代码上上吧 ssm的
1

	<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.6.0.RELEASE</version>
		</dependency>

2 在资源文件下添加相关配置文件
在这里插入图片描述
3 在web.xml 中 配置

<!-- springMVC核心配置 -->
	<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:configs/spring-web.xml,
				classpath:configs/spring-dao.xml,
				classpath:configs/spring-mvc.xml,
				classpath:configs/spring-shiro.xml
				classpath:configs/redis.xml
			</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>

	</servlet>
	<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>


	<!-- Spring MVC配置 -->
	<!-- druid 监控 -->
	<servlet>
		<servlet-name>druidStatView</servlet-name>
		<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>druidStatView</servlet-name>
		<url-pattern>/druid/*</url-pattern>
	</servlet-mapping>

	<!-- 解决中文编码问题 -->
	<!-- Spring字符集过滤器 -->
	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

4 redis.propweties文件

#单机模式 Redis settings
#redis.host=192.168.0.222
redis.host=127.0.0.1
redis.port=6379
redis.pass=Xl#mfb2019.com
redis.maxIdle=5
redis.maxActive=100
redis.maxWait=30000
redis.testOnBorrow=true
redis.dbIndex=1

#集群配置
#redis.sentinel.master.name=mymaster
#redis.sentinel.host1=192.168.2.128
#redis.sentinel.port1=26379
#redis.sentinel.host2=192.168.2.129
#redis.sentinel.port2=26379
#redis.sentinel.host3=192.168.2.130
#redis.sentinel.port3=26379

5 redis.xml

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
         http://www.springframework.org/schema/beans/spring-beans.xsd  
         http://www.springframework.org/schema/context  
         http://www.springframework.org/schema/context/spring-context.xsd">  
         
         
   	  <context:property-placeholder location="classpath:configs/redis.properties" ignore-unresolvable="true"/>
 	  <!-- 哨兵模式   begin-->
     <!-- <bean id="redisSentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
       		 <property  name="master">
      		    <bean class="org.springframework.data.redis.connection.RedisNode">
            		  <property  name="name" value="${redis.sentinel.master.name}"/>
          		</bean>
      	   	 </property>
             <property name="sentinels">
                <set>
                <bean class ="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.sentinel.host1}"></constructor-arg> 
                    <constructor-arg name ="port" value="${redis.sentinel.port1}"></constructor-arg>                   
                </bean>
                <bean class ="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.sentinel.host2}"/>
                    <constructor-arg name="port" value="${redis.sentinel.port2}"/>               
                </bean>
                <bean class ="org.springframework.data.redis.connection.RedisNode">                   
                    <constructor-arg name="host" value="${redis.sentinel.host3}"/>
                    <constructor-arg name="port" value="${redis.sentinel.port3}"/>               
                </bean>
                </set>
             </property>
        </bean>
    	<bean id="jeidsConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
   	     	<constructor-arg ref="redisSentinelConfiguration"/>
		</bean>
		<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
		    <property name="connectionFactory" ref="jeidsConnectionFactory" />  
		    <property name="keySerializer">
			 	<bean	class="org.springframework.data.redis.serializer.StringRedisSerializer" />
			</property>
	 		<property name="valueSerializer">
				<bean	class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
			</property>
		 </bean> -->
	  <!-- 哨兵模式   end-->
     <!-- 单机模式   begin -->        
            <!-- 连接池配置 最大空闲数、最大连接数、最长等待时间、连接是否可用 -->  
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">  
        <property name="maxIdle" value="${redis.maxIdle}" />  
        <property name="maxTotal" value="${redis.maxActive}" />  
        <property name="maxWaitMillis" value="${redis.maxWait}" />  
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />  
    </bean>  
      
   <!--  连接配置 地址、端口  -->
    <bean id="connectionFactory"  
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >  

        <property name="hostName" value="${redis.host}" />  
        <property name="port" value="${redis.port}" />
        <property name="password" value="${redis.pass}"/>
        <property name="poolConfig" ref="poolConfig" />  
        <property name="database" value="${redis.dbIndex}" />
    </bean>     
      
  <!-- 暴露一个redisTemplate 用作redis一系列操作  -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">  
        <property name="connectionFactory" ref="connectionFactory" />  
        <property name="keySerializer">
			<bean
				class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
	
		<property name="valueSerializer">
			<bean	class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
		</property>
    </bean>    
     <!-- 单机模式   end -->           
 </beans> 

6 根据集群获取jedis

 public static ShardedJedis getJedis () {
        JedisPoolConfig config =new JedisPoolConfig();//Jedis池配置
        config.setMaxTotal(500);//最大活动的对象个数
        config.setMaxIdle(1000 * 60);//对象最大空闲时间
        config.setMaxWaitMillis(1000 * 10);//获取对象时最大等待时间
        config.setTestOnBorrow(true);
        String hostA = "127.0.0.1";
        int portA = 6379;
//        String hostB = "192.168.0.115";
//        int portB = 6379;
        int db = 4 ;
        //设置使用的db和redis
        JedisShardInfo jedisShardInfo = new JedisShardInfo("127.0.0.1:6379/4");
//        jedisShardInfo.setPassword("");
        List<JedisShardInfo> jdsInfoList =new ArrayList<JedisShardInfo>(1);
//        JedisShardInfo infoA = new JedisShardInfo(hostA, portA);
//        infoA.setPassword("admin");
//        JedisShardInfo infoB = new JedisShardInfo(hostB, portB);
//        infoB.setPassword("admin");
//        jdsInfoList.add(infoA);
//        jdsInfoList.add(infoB);
        jdsInfoList.add(jedisShardInfo);
        ShardedJedisPool pool =new ShardedJedisPool(config, jdsInfoList);
        ShardedJedis jedis = pool.getResource();
        return jedis;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值