精华内容
下载资源
问答
  • iphone IMEI查询完整ID

    千次下载 热门讨论 2014-08-24 14:25:11
    IMEI快速查询苹果完整邮箱ID
  • 九种分布式ID生成算法详解

    万次阅读 2020-09-04 19:53:56
    一、分布式ID简介 1、什么是分布式ID? 在我们业务数据量不大的时候,单库单表完全可以支撑现有业务,数据再大一点搞个MySQL主从同步读写分离也能对付。 但随着数据日渐增长,主从同步也扛不住了,就需要对数据库...

    一、分布式ID简介

    1、什么是分布式ID?

    在我们业务数据量不大的时候,单库单表完全可以支撑现有业务,数据再大一点搞个MySQL主从同步读写分离也能对付。

    但随着数据日渐增长,主从同步也扛不住了,就需要对数据库进行分库分表,但分库分表后需要有一个唯一ID来标识一条数据,数据库的自增ID显然不能满足需求;例如我们的订单,需要有一个全局唯一标识的订单号,这个订单号就是分布式ID

    2、分布式ID需要满足那些条件?

      1. 全局唯一:必须保证ID是全局性唯一的,基本要求
      1. 高可用:
      1. 高性能:高可用低延时,ID生成响应要块,否则反倒会成为业务瓶颈
      1. 简单通用:拿来即用的原则

     

    二、 分布式ID都有哪些生成方式?

    1. UUID
    1. 数据库自增ID
    1. 数据库多主模式
    1. 号段模式
    1. Redis
    1. 雪花算法(SnowFlake)
    1. 滴滴出品(TinyID)
    1. 百度 (Uidgenerator)
    1. 美团(Leaf)
    1. MongoDB的ObjectID

     

    1、UUID

       ​String uuid = UUID.randomUUID().toString().replaceAll("-","");​

     

    优点:

    • 足够简单,只需要上面一行代码即可生成一个全局唯一的ID,

    缺点:

    • 因为UUID是随机的,没有具体的业务含义,如果用UUID作为订单号,是没有意义的,从这个UUID看不到跟订单有任何的关联关系,
    • UUID是一个无序的字符串,不具备趋势自增特性
    • 长度过长,存储以及查询对MySQL的性能消耗较大,如果用来作为主键,索引的性能会很差

     

    2、数据库自增ID

    基于数据库的​auto_increment​自增ID也可以生成一个分布式ID

     

    优点:

    • 实现简单,ID单调自增,数值类型查询速度快

    缺点:

    • 访问量激增时MySQL本身就是系统的瓶颈,用它来实现分布式服务风险比较大,不推荐!

     

    3、数据库集群模式

    为了解决第二种数据库单个DB的风险,可以采用数据库集群的模式来解决这个问题。

    • 可以使用主从模式解决单DB不满足高可用的问题,需要考虑的问题是:主从之间同步是存现时间延迟的,如果主DB挂了可能会有重复的ID生成(原因是什么? --  主从复制延时导致)
    • 如果担心一个主节点挂掉没法用,那就做双主模式集群,也就是两个Mysql实例都能单独的生产自增ID。需要考虑的问题是:1:两个MySQL实例的自增ID都从1开始,会生成重复的ID,怎么解决?2:后续DB扩容怎么办

     

     

    4、基于数据库的号段模式

     

    号段模式是当下分布式ID生成器的主流实现方式之一,号段模式可以理解为从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,具体的业务服务将本号段,生成1~1000的自增ID并加载到内存,典型的是TinyID。表结构如下:

    ​CREATE TABLE id_generator (​
    
    ​  id int(10) NOT NULL,​
    
    ​  max_id bigint(20) NOT NULL COMMENT '当前最大id',​
    
    ​  step int(20) NOT NULL COMMENT '号段的布长',​
    
    ​  biz_type    int(20) NOT NULL COMMENT '业务类型',​
    
    ​  version int(20) NOT NULL COMMENT '版本号',​
    
    ​  PRIMARY KEY (`id`)​
    
    ​) ​

     

    等这批号段ID用完,再次向数据库申请新号段,对​max_id​字段做一次​update​操作,​update max_id= max_id + step​,update成功则说明新号段获取成功,新的号段范围是​(max_id ,max_id +step]​。

    SQL语句:

    ​update id_generator set max_id = #{max_id+step} where biz_type = XXX​

     

    为了解决并发更新的问题,使用乐观锁控制,优化后的SQL:

    ​update id_generator set max_id = #{max_id+step}, version = version + 1 where version = # {version} and biz_type = XXX​

     

    优点:

    • 这种分布式ID生成方式不强依赖于数据库,不会频繁的访问数据库,对数据库的压力小很多

     

    5、基于Redis模式

    Redis也同样可以实现,原理就是利用redis的 incr命令实现ID的原子性自增。

    Redis是内存数据库,需要考虑持久化的问题,redis有两种持久化方式RDB和AOF

    • RDB:会定时打一个快照进行持久化,假如连续自增但redis没及时持久化,而这会Redis挂掉了,重启Redis后会出现ID重复的情况。
    • AOF:对每条写命令进行持久化,即使redis挂掉了也不会出现ID重复的情况,但由于incr命令的特殊性,会导致Redis重启恢复的数据时间过长。

     

    6、基于雪花算法(Snowflake)模式

    雪花算法(Snowflake)是twitter公司内部分布式项目采用的ID生成算法,开源后广受国内大厂的好评,在该算法影响下各大公司相继开发出各具特色的分布式生成器。
     

    Snowflake生成的是Long类型的ID,一个Long类型占8个字节,每个字节占8比特,也就是说一个Long类型占64个比特。

    Snowflake ID组成结构:​正数位​(占1比特)+ ​时间戳​(占41比特)+ ​机器ID​(占5比特)+ ​数据中心​(占5比特)+ ​自增值​(占12比特),总共64比特组成的一个Long类型。

    • 第一个bit位(1bit):Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。
    • 时间戳部分(41bit):毫秒级的时间,不建议存当前时间戳,而是用(当前时间戳 - 固定开始时间戳)的差值,可以使产生的ID从更小的值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年
    • 工作机器id(10bit):也被叫做​workId​,这个可以灵活配置,机房或者机器号组合都可以。
    • 序列号部分(12bit),自增值支持同一毫秒内同一个节点可以生成4096个ID

    Snowflake雪花算法最核心的是中间的10位工作机器ID的分配,做到自动生成workID,避免运维人员的去分配

     

    优点:

    • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
    • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
    • 可以根据自身业务特性分配bit位,非常灵活。

    缺点:

    • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

     

     

    根据算法的思路,使用java语言可以直接生成一个工具类,进行本地调用用来生成全局唯一的ID,

    参考github地址:https://github.com/beyondfengyu/SnowFlake

     

    7、滴滴出品(TinyID)

     

    ​Tinyid​由滴滴开发,Github地址:https://github.com/didi/tinyid

    ​Tinyid​是基于号段模式每个服务获取一个号段(1000,2000]、(2000,3000]、(3000,4000]

    1:具体实现原理:

    • tinyid是基于数据库发号算法实现的,简单来说是数据库中保存了可用的id号段,tinyid会将可用号段加载到内存中,之后生成id会直接内存中产生。
    • 可用号段在第一次获取id时加载,如当前号段使用达到一定量时,会异步加载下一可用号段,保证内存中始终有可用号段。当前号段使用完毕,下一号段会替换为当前号段。依次类推。

    2:架构图

    提供了两种接入方式:

    1. http接入,通过访问tinyid-server来生成ID,其中tinyid-server推荐部署到多个机房的多台机器,这种方式需要考虑网络的延时性
    1. 使用tinyid-client来获取id,

    优点:

    • id为本地生成(调用AtomicLong.addAndGet方法),性能大大增加
    • client对server访问变的低频,减轻了server的压力,无须担心网络延迟
    • 即便所有server挂掉,因为client预加载了号段,依然可以继续使用一段时间 

    缺点:

    • 如果client机器较多频繁重启,可能会浪费较多的id(原因是什么?

     

    8、百度 (Uidgenerator)

    UidGenerator是Java实现的, 基于Snowflake算法的唯一ID生成器。Github地址:https://github.com/baidu/uid-generator

    UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略, 从而适用于docker等虚拟化环境下实例自动重启、漂移等场景, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费。

    具体算法实现:

    Snowflake算法:

    Snowflake算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)。默认采用上图字节分配方式:

    • sign(1bit)
    • 固定1bit符号标识,即生成的UID为正数。
    • delta seconds (28 bits)
    • 当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年
    • worker id (22 bits)
    • 机器id,最多可支持约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略。
    • sequence (13 bits)
    • 每秒下的并发序列,13 bits可支持每秒8192个并发。

     

    9、美团(Leaf)

    Leaf由美团开发,github地址:https://github.com/Meituan-Dianping/Leaf

    同时支持号段模式和Snowflake算法模式,可以切换使用。

    1:Leaf-segment号段模式:

    在使用数据库的方案上,做了如下改变: 

    1. 原方案每次获取ID都得读写一次数据库,造成数据库压力大。改为利用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力
    2. 各个业务不同的发号需求用biz_tag字段来区分,每个biz-tag的ID获取相互隔离,互不影响

    号段模式的具体实现思路同TinyID一样

     

    2:Leaf-snowflake雪花算法:

    完全沿用snowflake方案的bit位设计,即是“1+41+10+12”的方式组装ID号。为了解决集权下workID的问题,美团的Leaf-snowflake跟百度不一样,百度是通过数据库来生成workID,而美团是通过Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID,所以Leaf是依赖ZK服务的。

    特点:

    1. 弱依赖ZooKeeper:除了每次会去ZK拿数据以外,也会在本机文件系统上缓存一个workerID文件。当ZooKeeper出现问题,恰好机器出现问题需要重启时,能保证服务能够正常启动。这样做到了对ZK的弱依赖
    2. 解决时钟问题:因为这种方案依赖时间,如果机器的时钟发生了回拨,那么就会有可能生成重复的ID号,需要解决时钟回退的问题。

     

    展开全文
  • Mybatis-Plus雪花id的使用以及解析机器 ID 和数据标识 ID概述结构源码Mybatis-Plus使用雪花id1.引入Mybatis-Plus依赖2.在application.yml配置文件中增加如下配置项3.原有的mapper接口增加继承BaseMapper接口4.实体类...

    概述

    分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。

    有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。

    而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。

    结构

    snowflake的结构如下(每部分用-分开):

    0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

    第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年),然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点) ,最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

    一共加起来刚好64位,为一个Long型。(转换成字符串后长度最多19)

    snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID。

    源码

    /**
     * Twitter_Snowflake<br>
     * SnowFlake的结构如下(每部分用-分开):<br>
     * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
     * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
     * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
     * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
     * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
     * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
     * 加起来刚好64位,为一个Long型。<br>
     * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
     */
    public class SnowflakeIdWorker {
    
        // ==============================Fields===========================================
        /** 开始时间截 (2015-01-01) */
        private final long twepoch = 1420041600000L;
    
        /** 机器id所占的位数 */
        private final long workerIdBits = 5L;
    
        /** 数据标识id所占的位数 */
        private final long datacenterIdBits = 5L;
    
        /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
        private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    
        /** 支持的最大数据标识id,结果是31 */
        private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    
        /** 序列在id中占的位数 */
        private final long sequenceBits = 12L;
    
        /** 机器ID向左移12位 */
        private final long workerIdShift = sequenceBits;
    
        /** 数据标识id向左移17位(12+5) */
        private final long datacenterIdShift = sequenceBits + workerIdBits;
    
        /** 时间截向左移22位(5+5+12) */
        private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    
        /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
        private final long sequenceMask = -1L ^ (-1L << sequenceBits);
    
        /** 工作机器ID(0~31) */
        private long workerId;
    
        /** 数据中心ID(0~31) */
        private long datacenterId;
    
        /** 毫秒内序列(0~4095) */
        private long sequence = 0L;
    
        /** 上次生成ID的时间截 */
        private long lastTimestamp = -1L;
    
        //==============================Constructors=====================================
        /**
         * 构造函数
         * @param workerId 工作ID (0~31)
         * @param datacenterId 数据中心ID (0~31)
         */
        public SnowflakeIdWorker(long workerId, long datacenterId) {
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
        }
    
        // ==============================Methods==========================================
        /**
         * 获得下一个ID (该方法是线程安全的)
         * @return SnowflakeId
         */
        public synchronized long nextId() {
            long timestamp = timeGen();
    
            //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }
    
            //如果是同一时间生成的,则进行毫秒内序列
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                //毫秒内序列溢出
                if (sequence == 0) {
                    //阻塞到下一个毫秒,获得新的时间戳
                    timestamp = tilNextMillis(lastTimestamp);
                }
            }
            //时间戳改变,毫秒内序列重置
            else {
                sequence = 0L;
            }
    
            //上次生成ID的时间截
            lastTimestamp = timestamp;
    
            //移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - twepoch) << timestampLeftShift) //
                    | (datacenterId << datacenterIdShift) //
                    | (workerId << workerIdShift) //
                    | sequence;
        }
    
        /**
         * 阻塞到下一个毫秒,直到获得新的时间戳
         * @param lastTimestamp 上次生成ID的时间截
         * @return 当前时间戳
         */
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    
        /**
         * 返回以毫秒为单位的当前时间
         * @return 当前时间(毫秒)
         */
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    
        //==============================Test=============================================
        /** 测试 */
        public static void main(String[] args) {
            SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
            for (int i = 0; i < 1000; i++) {
                long id = idWorker.nextId();
                System.out.println(Long.toBinaryString(id));
                System.out.println(id);
            }
        }
    }
    

    以上资料转载自该博客,点我!

    Mybatis-Plus使用雪花id

    注意:以下配置是在SpringBoot2.1.4版本以及在mybatis已经可以使用的基础上做的升级配置!

    1.引入Mybatis-Plus依赖(3.1.1版本目前有些问题,建议使用3.1.0版本)

    	<dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.1.0</version>
    	</dependency>
    

    2.在application.yml配置文件中增加如下配置项

    mybatis-plus:
      #mapper-locations: classpath:mybatis/**/*Mapper.xml
      # 在classpath前添加星号可以使项目热加载成功
      mapper-locations: classpath*:mybatis/**/*Mapper.xml
      #实体扫描,多个package用逗号或者分号分隔
      typeAliasesPackage: com.nis.project
      global-config:
        #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
        id-type: 3
        #机器 ID 部分(影响雪花ID)
        workerId: 1
        #数据标识 ID 部分(影响雪花ID)(workerId 和 datacenterId 一起配置才能重新初始化 Sequence)
        datacenterId: 18
        #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
        field-strategy: 2
        #驼峰下划线转换
        db-column-underline: true
        #刷新mapper 调试神器
        refresh-mapper: true
        #数据库大写下划线转换
        #capital-mode: true
        #序列接口实现类配置
        #key-generator: com.baomidou.springboot.xxx
        #逻辑删除配置(下面3个配置)
        logic-delete-value: 0
        logic-not-delete-value: 1
        #自定义SQL注入器
        #sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
        #自定义填充策略接口实现
        #meta-object-handler: com.baomidou.springboot.xxx
      configuration:
        map-underscore-to-camel-case: true
        cache-enabled: false
        # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    

    3.原有的mapper接口增加继承BaseMapper接口

    public interface UserMapper extends BaseMapper<User>
    

    4.实体类增加注解

    在User实体类上添加@TableId的注解用来标识实体类的主键,以便插件在生成主键雪花Id的时候找到哪个是主键。

    	@TableId
        @JsonFormat(shape = JsonFormat.Shape.STRING)
        private Long userId;
    

    5.分页配置

    5.1 添加mybatis的一个配置类(不添加,则分页数据中的page参数会异常)

    package com.nis.framework.config;
    
    import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    /**
     * Mybatis-Plus插件配置类
     *
     * @author lwj
     */
    @EnableTransactionManagement
    @Configuration
    @MapperScan({"com.nis.project.*.mapper","com.nis.project.*.*.mapper"})
    public class MybatisPlusConfig {
    
        /**
         * 分页插件
         */
        @Bean
        public PaginationInterceptor paginationInterceptor() {
            return new PaginationInterceptor();
        }
    }
    
    

    5.2 java代码示例

    Controller类中添加page分页对象。

    	@GetMapping("/list")
        @ResponseBody
        public Msg list(User user, HttpServletRequest request) {
            Map<String, Object> params = MapDataUtil.convertDataMap(request);
            user.getParams().putAll(params);
            Page<User> page = startMybatisPlusPage(user);
            IPage<User> userIPage = userService.selectUserList(page, user);
            return Msg.success(userIPage);
        }
    

    ServiceImpl中的具体方法

    	@Override
        @DataScope(tableAlias = "b")
        public IPage<User> selectUserList(Page<User> page, User user) {
            return UserMapper.selectUserList(page, user);
        }
    

    mapper接口中的代码

    IPage<User> selectUserList(@Param("pg") Page<User> page, @Param("ps") User user);
    

    mybaits的xml文件中写的sql代码

        <select id="selectUserList" resultType="com.nis.project.system.user.domain.User" parameterType="com.nis.project.system.user.domain.User">
            select
            a.user_id,
            a.dept_id,
            a.login_name,
            a.user_name,
            a.email,
            a.phone_number,
            a.sex,
            a.avatar,
            a.status,
            a.login_ip,
            a.login_date,
            a.order_num,
            b.dept_name
            from sys_user a
            left join sys_dept b on b.dept_id = a.dept_id
            where 1=1
            <if test="ps.loginName != null and ps.loginName != ''">and a.login_name like concat('%', #{ps.loginName}, '%')</if>
            <if test="ps.userName != null and ps.userName != ''">and a.user_name like concat('%', #{ps.userName}, '%')</if>
            <if test="ps.deptId != null and ps.deptId != ''">and b.dept_id = #{ps.deptId}</if>
            <if test="ps.email != null and ps.email != ''">and a.email = #{ps.email}</if>
            <if test="ps.phoneNumber != null and ps.phoneNumber != ''">and a.phone_number like concat('%', #{ps.phoneNumber}, '%')</if>
            <if test="ps.params.deptIds != null and ps.params.deptIds != ''">and find_in_set(a.dept_id,#{ps.params.deptIds})</if>
            <if test="ps.status != null and ps.status != ''">and a.status = #{ps.status}</if>
            <!-- 数据范围过滤 -->
            ${ps.params.dataScope}
            order by b.ancestors,a.order_num
        </select>
    

    生成雪花ID以及解析雪花ID的机器ID和数据中心ID

    1.生成雪花ID

    这里不再阐述,直接贴上生成的一个雪花ID;1146667501642584065

    2.解析雪花ID的机器ID和数据中心ID

    SELECT (1146667501642584065>>12)&0x1f as workerId,(1146667501642584065>>17)&0x1f as datacenterId;
    

    结果图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 有效用户id、实际用户id、设置用户id概念更改用户ID以下内容均来自https://www.cnblogs.com/stemon/p/5287631.html的内容实例分析一: 概念 实际用户id(RUID,进程特有的概念): 在开机时,你输入的账号的对应id...

    概念

    实际用户id(RUID,进程特有的概念): 在开机时,你输入的账号的对应id就是实际用户uid,说白了就是登录号,站在用户的角度上看。
    有效用户id(EUID,进程特有的概念): 一般和RUID相同,站在操作系统的角度上看,用于给操作系统判断某个进程是否拥有操作某个文件的权限。
    设置用户id(SUID,set user id,文件特有的概念): 站在文件的角度上看,文件权限有r-w-x-s-t类型,其中的s表示SUID是打开状态(即可用状态)。如果一个文件的权限位里面没有s,则表示SUID是关闭状态(即不可用状态)。注意:SUID只能用于可执行文件,其作用是修改EUID(有效用户id)
    保存的设置用户id(SSUID,saved SUID,《Unix高级环境编程 第3版》的8.11节里面定义的,进程特有的概念),也有博客称为保留的设置用户id: EUID(有效用户id)的备份,既然是备份,则应该是恢复的作用。SSUID由linux的exec()函数保存,可以把SSUID理解为exec()函数里面的一个局部变量。exec()的执行逻辑大概如下(自己猜测的,为了好理解SSUID的作用):

    // 这里假设 "可执行文件"为/etc/passwd, 该文件的权限位-rwsr-xr-x,文件的拥有者是root
    void exec("可执行文件") {
    	SSUID = EID;
    	进程的EID = 可执行文件用户(即root用户);
    	执行"可执行文件";
    	//此时调用本函数的进程已经拥有root权限了
    	
    	EID = SSUID;
    	
    }
    

    文件特有的概念:
    **FUID(file uid):**文件的拥有者
    **FGID(file gid):**文件的拥有者所在组的组id
    设置用户id(SUID,set user id,文件特有的概念): 站在文件的角度上看,文件权限有r-w-x-s-t类型,其中的s表示SUID是打开状态(即可用状态)。如果一个文件的权限位里面没有s,则表示SUID是关闭状态(即不可用状态)。注意:SUID只能用于可执行文件,其作用是修改EUID(有效用户id)

    总结:RUID、EUID、SSUID都是针对进程的,即站在进程的角度,即进程特有的概念。而SUID则是针对文件的,即站在文件的角度,或者说是SUID是可执行文件特有的概念!为什么要修改进程的EUID?目的是想让进程在某一时刻能够拥有特殊的权限。

    更改用户ID

    进程特有的概念
    一个文件有9个文件权限位,比如/etc/passwd的9个权限位是 rwsr-xr-x, 其中的s就是SUID(设置用户ID位),说明/etc/passwd的设置用户ID位是打开状态。

    在这里插入图片描述

    以下内容均来自https://www.cnblogs.com/stemon/p/5287631.html的内容

    对于一个普通文件,有三个ID,这三个ID对应三组权限,这三组权限控制着进程对该文件的访问权限。
    在这里插入图片描述
    对于linux系统,某个用户登录后,创建一个文件,那个这个文件的用户ID就是这个用户的ID。该用户创建的所有的进程都可以访问这个文件,因为该用户创建的进程的实际用户ID和有效用户ID都是这个用户的ID。但是当一个用户创建的进程要去访问其他用户创建的文件的时候,就需要用到有效用户ID的改变,来能够有权限访问这个文件。

    实例分析一:

    如何在权限不够的情况下执行特权权限,具体的方法就是更改进程的有效用户ID。
    对于linux系统来说,用户的密码都存放在/etc/shadow文件下,查看一下这个文件的权限:

    在这里插入图片描述

    假如我是一个普通的用户,显然我是可以修改我自己的密码的,通过passwd命令,无可厚非,自己修改自己的密码当然是允许的。

    但是仔细想想有没有什么不对的地方,作为一个普通的用户登录后,我的所有的进程的实际用户ID和有效用户ID都应该是我自己(这个用户)的UID。从上面对/etc/shadow文件用户ID和所属的组ID看,我不具备修改这个文件的权限,那么执行passwd命令是怎么修改我的密码的呢?

    根据上面所讲的知识,决定进程对文件的访问权限的是执行操作时的有效用户ID,所以在执行passwd命令时,进行的有效用户ID肯定不是我这个普通用户的UID,一定被修改过了。

    还有一点,在执行passwd这个命令的时候,在磁盘上肯定有一个可执行的文件,下面看看这个可执行文件的权限:
    在这里插入图片描述

    我们看到了一个s,这就是使用户设置ID位有效,上面说过这个位的作用就是修改执行这个可执行文件的进程的有效用户ID,那么我么来看看他是怎样修改执行passwd命令的进程的有效用户ID的。

    首先看一下命令的执行的过程,当普通用户执行passwd命令时,shell会fork出一个子进程,此时该进程的有效用户ID还是这个普通用户的ID,然后exec程序执行/usr/bin/passwd这个例程(可执行文件)。通过上面的表我们就能知道,exec发现/usr/bin/passwd这个可执行文件有SUID为,于是会把进程的有效用户ID设置成可执行文件的用户ID,显示是root,此时这个进程就获得了root的权限,得到了读写/etc/shadow文件的权限,从而普通用户可以完成密码的修改。exec进程退出后,会恢复普通用户的有效用户ID为实际用户ID(也就是该普通用户的ID),这样就保证了不会是普通用户一直具有root权限。

    在exec时,修改进程的有效用户ID为文件的用户ID,还是恢复进程的有效用户ID为进程的实际用户ID,这些都是linux内核完成的,用户是不能干预这些步骤的。

    这样的过程既实现了普通用户权限的暂时的提升,不让普通用户长久的拥有root权限。同时即使在普通用户拥有root权限的这段短暂的时间里,普通用户也不能为所欲为,因为普通用户的这个进程必须按照/usr/bin/passwd这个可执行文件的指令内容执行,不能干之外的任何事情,而这个可执行文件又是root事先写好的,多么完美的设计啊~~

    这就是设置用户ID为的作用,它的存在就是为了普通用户能够在某些时候获取一段时间的超级权限,但是你可能疑问,为什么不能用setuid直接修改呢?

    如果这里可以用setuid直接修改进程的有效用户ID来获得特权权限,那么整个系统的超级权限就不可控制了,这违背了最小权限模型。所以linux的设计是:setuid在非特权用户下面,有效用户ID只能设置成为实际用户ID或者保存的设置用户ID。保存的设置用户ID又是有效用户ID的副本,有效用户ID只能是实际用户ID或者文件的所有者ID(在设置了保存用户ID位时)。

    这样你就不能将有效用户ID设置成随意的值,所以对普通用户创建的任何文件,如果没有得到超级用户的授权,那么无论怎么编写代码来设置运行进程的有效用户ID或者设置用户ID位,由于这个可执行文件是普通用户自己编写的,所以权限根本没有任何改变。这里就是说只有root自己创建的可执行文件并且设置了用户ID位,才能够提升执行该可执行文件的进程的权限。

    展开全文
  • 还可以参考这位@TableId(value = “id“,type = IdType.AUTO) 设置后无效的解决办法的解决方式,也比较玄学。 思考 好吧,其实也不玄学,说下自己的思考。 我们创建表的时候,表id字段是设置自动增长的,并且主键...

    异常

    设置@TableId注解后,还是无法做到数据库的主键自增长。

    原因

    不清楚,解决方法也比较玄学。

    解决

    在数据库中将该表删除掉,重新创建表并赋予数据,当然实体类还是要在id字段上添加@TableId注解

    还可以参考这位@TableId(value = “id“,type = IdType.AUTO) 设置后无效的解决办法的解决方式,也比较玄学。

    思考

    好吧,其实也不玄学,说下自己的思考。

    我们创建表的时候,表id字段是设置自动增长的,并且主键id也是正常的,如下(表中已有五条记录):

    然后我们使用MybatisPlus的insert()方法来插入数据,我们第一次插入的时候是没有在id字段上添加@TableId注解,也就是说我们按照mybatisplus方式设置的主键值,是一个很大很大的数字。

    执行该insert()方法后,我们可以在数据库表中看到自增id变成了个很大的值

    查看表结构

    此时我们肯定要查找资料,知道可以通过设置@TableId(type=IdType.AUTO)来让主键自动递增,于是我们这样做了

    再度执行insert()方法来插入数据

    欸,发现还是没有自动递增,我们期望的应该id是6,但事实上不是,那么真的自增没有生效吗?不是的,我们还是来看表的结构,发现"自动递增"这个字段的值就是刚才插入的id值加1。

    也就是我们设置的自动递增生效了,但是由于第一次使用MybatisPlus的insert()方法插入导致自增id值变成很大的一个数,那么在表结构中"自动递增"这个字段的值也变成了很大的数加1,也就是下一个id的值。

    我们插入主键自动增长也就是获取这个值,所以我们即使设置了@TableId注解,也失败了,因为"自动递增"字段已经变成了很大的数,那么下一次递增,也是在这个已有的数上加1。

    而我所提供的解决方法就是重新构建表结构,那么"自动递增"这个字段的值又会恢复正常,而Java程序中使用了@TableId注解,也会插入成功。

    也就是说我所提供的方法就是为了重置"自动递增"这个字段

    注意,我也视图直接通过修改这个字段或者使用"ALTER TABLE `tb_user` AUTO_INCREMENT=9;"语句来修改自动递增这个字段,但是失败了,因为这个字段只能改为比当前值大的数,不能改为比当前值小的数

    所以我暂时只想到这种方法:删除表,重新创建表结构。

    展开全文
  • MySQL - SELECT LAST_INSERT_ID() 使用总结

    万次阅读 2019-03-27 16:22:36
    比如:新增用户信息,但是 用户信息包含的一部分信息在其它表中,这时候你需要通过用户id来进行add,这时候你如果用普通的insert,只会返回0和1,这时候就需要用到这个函数了。 目录 函数介绍 举个例子 官方...
  • CAN笔记(17) 预定义报文ID

    万次阅读 2019-09-11 08:45:12
    网络管理NMT、特殊协议报文、过程数据对象PDO和服务数据对象SDO的报文ID分配
  • 常见分布式全局唯一ID生成策略及算法的对比

    万次阅读 多人点赞 2018-03-31 09:30:49
    全局唯一的 ID 几乎是所有系统都会遇到的刚需。这个 id 在搜索, 存储数据, 加快检索速度 等等很多方面都有着重要的意义。工业上有多种策略来获取这个全局唯一的id,针对常见的几种场景,我在这里进行简单的总结和...
  • 本文通过在日志中打印唯一的traceId来实现每次调用的追踪。 2.关键思路 2.1.MDC 日志追踪目标是每次请求级别的,也就是说同一个接口的每次请求,都应该有不同的traceId。 每次接口请求,都是一个单独的线程,所以...
  • Cookie和Session、SessionID的那些事儿

    万次阅读 多人点赞 2019-06-16 14:40:31
     客户端用cookie保存了sessionID,当我们请求服务器的时候,会把这个sessionID一起发给服务器,服务器会到内存中搜索对应的sessionID,如果找到了对应的 sessionID,说明我们处于登录状态,有相应的权限;...
  • android @id和@+id的区别

    万次阅读 2018-05-29 08:40:46
    今天,简单讲讲android里关于@id和@+id的区别。之前,自己在布局里无论什么情况都使用@+id,可是后来发现有些代码用的是@id,自己不知道这两者之间有什么区别。于是就在网上查找资料,最终是解决了问题。这里记录...
  • Android 动态添加View 并设置id

    万次阅读 多人点赞 2018-03-22 16:55:27
    然后通过setId()方法引用这个ids.xml资源文件中的id就行了 textView1 .setId (R .id .text _view_1) ; MainActivity.java package com .yechaoa .addview ; import android .graphics .Color ; ...
  • python3 获取 进程id 线程id

    万次阅读 2020-01-21 09:52:53
    1.获取线程id import threading # 1 获取线程ID,NAME t = threading.currentThread() #线程ID print('Thread id : %d' % t.ident) #线程NAME print('Thread name : %s' % t.getName()) 输出: Thread id : ...
  • 文档来源,Mybatis-Plus 2.X:... ... 不想看过程的直接可以看结尾的解决方法。 首先看官方介绍: ...官方说这里默认使用ID_WORKER策略 实际测试时却一直报错,...
  • 我们在实际编程过程中会经常遇到需要用唯一ID的场合,这些唯一ID还会存到数据库中以便于我们将来进行查询。 例如用户编号、订单编号、客户编号等等,几乎凡是需要用来严格划分用户数据归属性的地方就需要用到唯一ID...
  • 基因ID

    万次阅读 2017-12-16 14:45:52
    各类基因ID转换基因ID分类 Ensembl的目标是为遗传学家,分子生物学家和其他研究人员研究我们自己的物种和其他脊椎动物和模式生物的基因组而提供集中的资源。Ensembl是几个知名的基因组浏览器之一,用于检索基因组学...
  • 分布式ID生成 - 雪花算法

    万次阅读 多人点赞 2018-08-30 00:00:03
    雪花算法是一种生成分布式全局唯一ID的经典算法,关于雪花算法的解读网上多如牛毛,大多抄来抄去,这里请参考耕耘的小象大神的博客ID生成器,Twitter的雪花算法(Java) 网上的教程一般存在两个问题: 1. 机器ID...
  • 分布式自增ID生成算法 - 雪花算法(SnowFlake)

    万次阅读 多人点赞 2019-04-02 10:51:30
    1、SnowFlake算法生成id的结果是一个64bit大小的整数,它的结构如下图: ● 1位,不用。二进制中最高位为1的都是负数,但是我们生成的id一般都使用整数,所以这个最高位固定是0 ● 41位,用来记录时间戳(毫秒...
  • Gene id 转换(四种基因各种id转换方法)

    万次阅读 多人点赞 2019-04-17 13:25:44
    #这个是可以选择的,选择不同,转换的ID类型也不一样 ifnotfound=NA) 转换成Symbol ID head(mySymbols) class(mySymbols) 第三种方法 geneIDselect (org.Hs.eg.db, #.db是这个芯片数据对应的注释包 keys=gene, ...
  • 修改用户ID和用户组ID

    千次阅读 2018-12-01 15:21:27
    假定原用户和用户组id如下: 用户 admin 500 组 admin 500 要修改成用户和组id如下: 用户 admin 1000 组 admin 1000 修改用户ID usermod -u 1000 admin 修改组ID groupmod -g 1000 admin 修改文件权限 find / -...
  • 基因ID命名及相互转换

    万次阅读 2019-11-01 19:22:10
    由于ID来自于不同的数据库,或者说命名的意图不同,所以对于同一个基因,总是有多个不同的ID,最常见的比如entrze ID、ensembl ID、HGNC ID、refseq ID等。 Entrze ID:是美国NCBI数据库中的基因标识符,通常是由纯...
  • 如何修改CSDN的ID

    万次阅读 多人点赞 2019-10-14 10:35:57
    如何修改CSDN的ID号 问题描述: 想在CSDN注册一个账号写博客,打开CSDN官网发现,只有直接账号密码登录或者第三方登录,就是没有注册的按钮,导致ID号是官网自动分配的,说实话是很ugly,实在是很丑,简直要逼死...
  • 在Java项目中使用traceId跟踪请求全流程日志

    万次阅读 多人点赞 2018-06-25 02:09:15
    因此计划对项目日志打印进行一些小改造,使用一个traceId跟踪请求的全部路径,前提是不修改原有的打印方式。 简单的解决思路 想要跟踪请求,第一个想到的就是当请求来时生成一个traceId放在ThreadLoc...
  • mysql插入数据,获取最新插入的ID(自增列)的思路和python获取MySQL自增ID代码三种实现 mysql本身有一个列可以做自增长字段,mysql在插入一条数据后,如何能获得到这个自增id的值呢? (1)方法一 是使用last_...
  • id是选择器data-id只是行内存放数据的一个标签,跟input里面的value作用是一样的同时在HTML5 中增加了一项新功能是 自定义数据属性 ,也就是 data-* 自定义属性。在HTML5中我们可以使用以 data- 为前缀来设置我们...
  • 话不多说,直接开整!...因为返回的id会自动注入进去,所以得选实体类来接收自增的id。 @Data public class SignIn implements Serializable { private long id;//注意id为long类型 private String owner;
  • 很多时候,我们需要获取到刚刚插入到数据库的数据的id是什么,这里的id可能有两种情况,一种是自增长的id,另外一种情况是用户自定义的id,例如生成的uuid。 思路:insert完成之后再去查询得到id,这样显然不行,很...
  • @TableId(value = “id”,type = IdType.AUTO) @TableId(value = “id”,type = IdType.INPUT) 刚开始自增一直是32位的 @TableId(value = "id",type = IdType.AUTO) private Long id; id数据表中为bigint 类中...
  • 黑苹果 声卡驱动 ID注入

    万次阅读 2020-05-10 19:26:20
    黑苹果 声卡驱动 ID注入1,黑苹果 声卡驱动 ID注入 1,黑苹果 声卡驱动 ID注入 <key>Audio</key> <dict> <key>AFGLowPowerState</key> <true/> <key>Inject...
  • IdType是一个枚举类,定义了生成ID的类型 AUTO 数据库ID自增 INPUT 用户输入ID ID_WORKER 全局唯一ID,Long类型的主键 ID_WORKER_STR 字符串全局唯一ID UUID 全局唯一ID,UUID类型的主键 NONE 该类型为未设置主键...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,485,313
精华内容 3,794,125
关键字:

iD