全局唯一的id生成的技术方案有很多,业界比较有名的是UUID、redis、Twitter的snowflake算法、美团Leaf算法。  
我们重点来讲解redis生成id算法。

全局唯一id必须具备什么特点?
1. 全局唯一性
:不能出现重复的ID,最基本的要求。
2. 单调递增:保证下一个ID一定大于上一个ID。
3. 趋势递增:在一段时间内,生成的ID是递增的趋势。如:在一段时间内生成的ID在【0,1000】之间,过段时间生成的ID在【1000,2000】之间。
   但在【0-1000】区间内的时候,ID生成有可能第一次是12,第二次是10,第三次是14。
   
4. 信息安全:如果ID是连续递增的,恶意用户就可以很容易的窥见订单号的规则,从而猜出下一个订单号,
   如果是竞争对手,就可以直接知道我们一天的订单量。所以在某些场景下,需要ID无规则。

第2、4两个需求是互斥的,无法同时满足。
同时,在大型分布式网站架构中,除了需要满足ID生成自身的需求外,还需要ID生成系统可用性极高。想象以下,如果ID生成系统瘫痪,
那么整个业务无法进行下去,那将是一次灾难。

因此,做一个全局唯一id生成系统必须满足以下特点:
1. 高可用
,可用性达到5个9或4个9。
2. 高QPS,性能不能太差,否则容易造成线程堵塞。

基于Redis INCR 命令生成 分布式全局唯一id
INCR 命令主要有以下2个特征:
1. Redis的INCR命令具备了“INCR AND GET”的原子操作,即增加并返回结果的原子操作。这个原子性很方便我们实现获取ID.
2. Redis是单进程单线程架构,INCR命令不会出现id重复.
基于以上2个特性,我们可以采用INCR命令来实现分布式全局ID生成。
 

import cn.jiqistudy.redis_1.Redis1Application;
import cn.jiqistudy.redis_1.pojo.User;
import cn.jiqistudy.redis_1.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Redis1Application.class)
public class Test_8 {

    private static final Logger log = LoggerFactory.getLogger(UserService.class);

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String ID_KEY = "id:generator:user";

    /**
     * 生成全局唯一id
     */
    @Test
    public void incrementId() {
        for (int i = 0; i <100 ; i++) {
            //步骤1:生成分布式id
            long id=this.stringRedisTemplate.opsForValue().increment(ID_KEY);
            System.out.println(id);

            //全局id,代替数据库的自增id
            User user = new User();
            user.setId(id);

            //步骤2:取模,计算表名
            //类似于海量的数据,例如淘宝一般是分为1024张表,这里为了演示方便,只分为8张表。
            int table=(int)id % 8;
            String tablename="user_"+table;

            log.info("插入表名{},插入内容{}",tablename,user);
        }
    }
}