PHP QR CODE生成二维码
一、为什么要全局唯一?
我们在对数据库集群作扩容时,为了保证负载的平衡,需要在不同的Shard之间进行数据的移动, 如果主键不唯一,我们就没办法这样随意的移动数据。起初,我们考虑采用组合主键来解决这个问题。 一般会以user_id
和一个自增的photo_id
来作为主键,这的确能解决移动数据可能带来的主键冲突问题, 但是在多台MySQL服务器组成的水平分库场景下,每个shard都是master-master的复制方式, 以保证每个Shard一直可写。master-master复制方式必须保证在两台服务器上各自插入的数据有不同的主键, 不然当复制到另外一台时就会出现主键重复错误。如果我们保证主键全局唯一,就自然的解决了这个问题。
在分布式系统存在多个 Shard 的场景中, 同时在各个 Shard 插入数据时, 怎么给这些数据生成全局的 unique ID?
在单机系统中 (例如一个 MySQL 实例), unique ID 的生成是非常简单的, 直接利用 MySQL 自带的自增 ID 功能就可以实现.
但在一个存在多个 Shards 的分布式系统 (例如多个 MySQL 实例组成一个集群, 在这个集群中插入数据), 这个问题会变得复杂, 所生成的全局的 unique ID 要满足以下需求:
保证生成的 ID 全局唯一
今后数据在多个 Shards 之间迁移不会受到 ID 生成方式的限制
生成的 ID 中最好能带上时间信息, 例如 ID 的前 k 位是 Timestamp, 这样能够直接通过对 ID 的前 k 位的排序来对数据按时间排序
生成的 ID 最好不大于 64 bits
生成 ID 的速度有要求. 例如, 在一个高吞吐量的场景中, 需要每秒生成几万个 ID (Twitter 最新的峰值到达了 143,199 Tweets/s, 也就是 10万+/秒)
整个服务最好没有单点
如果没有上面这些限制, 问题会相对简单, 例如:
mysql多台机器上,可选的解决方案有:
1、使用auto_increment_increment和auto_increment_offset
这两台服务器变量可以让mysql以期望的值和偏移量来增加AUTO_INCREMENT列的值。
例如,让一台服务器生成的值为奇数,另一台服务器生成的值为偶数。将奇数那台机器的步长设置为1,将偶数台机器的步长设置为2。
2、全局节点中创建表(单点问题)
在单个MySQL数据库的应用中一般设置一个自增的字段就可以了,而在水平分库的设计当中,这种方法显然不能保证全局唯一。 那么我们可以单独建立一个库用来生成ID,在Shard中的每张表在这个ID库中都有一个对应的表,而这个对应的表只有一个字段, 这个字段是自增的。当我们需要插入新的数据,我们首先在ID库中的相应表中插入一条记录,以此得到一个新的ID, 然后将这个ID作为插入到Shard中的数据的主键。这个方法的缺点就是需要额外的插入操作,如果ID库变的很大, 性能也会随之降低。所以一定要保证ID库的数据集不要太大,一个办法是定期清理前面的记录。
如果ID库变的很大,单库的写性能是瓶颈的话,可以将increment的步长设置大点,然后在内存中分配。(缺点时,服务重启后,有些id将会丢弃,第4点有提到)
3、使用memcached的incr()函数
在memcached的API中有一个incr()函数,可以自动增长一个数字并返回结果。另外也可以使用Redis。
4、批量分配数字
应用可以从一个全局节点中请求一批数字,用完后再申请。(减轻唯一ID生成服务的压力,但会浪费一个数字,当申请的数字没有用完时)
5、使用复合值
可以使用一个复合值来做唯一ID,例如,分片号和自增数的组合。
6、使用GUID,
例如,在java中直接利用 UUID.randomUUID() 接口来生成 unique ID,可以解决不重复问题。
但UUID的缺点明显有:
1、UUID可读性差, 当然这个不是关键所在;
2、更重要的原因还是性能。UUID的生成没有顺序性,所以在写入时, 需要随机更改索引的不同位置,这就需要更多的IO操作,如果索引太大而不能存放在内存中的话就更是如此。 而UUID索引时,一个key需要32个字节(当然如果采用二进制形式存储的话可以压缩到16个字节), 因此整个索引也会相对比较大,性能差。
7、Flickr 的做法 (http://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/). 但他这个方案 ID 中没有带 Timestamp, 生成的 ID 不能按时间排序
如果使用全局分配器来产生唯一ID,要注意避免单点争用成为应用的性能瓶颈。
在要满足前面 6 点要求的场景中, 怎么来生成全局 unique ID 呢?
Twitter 的 Snowflake 是一种比较好的做法. 下面主要介绍 Twitter Snowflake, 以及它的变种
二, Twitter Snowflake
https://github.com/twitter/snowflake
Snowflake 生成的 unique ID 的组成 (由高位到低位):
41 bits: Timestamp (毫秒级)
10 bits: 节点 ID (datacenter ID 5 bits + worker ID 5 bits)
12 bits: sequence number
一共 63 bits (最高位是 0)
unique ID 生成过程:
10 bits 的机器号, 在 ID 分配 Worker 启动的时候, 从一个 Zookeeper 集群获取 (保证所有的 Worker 不会有重复的机器号)
41 bits 的 Timestamp: 每次要生成一个新 ID 的时候, 都会获取一下当前的 Timestamp, 然后分两种情况生成 sequence number:
如果当前的 Timestamp 和前一个已生成 ID 的 Timestamp 相同 (在同一毫秒中), 就用前一个 ID 的 sequence number + 1 作为新的 sequence number (12 bits); 如果本毫秒内的所有 ID 用完, 等到下一毫秒继续 (这个等待过程中, 不能分配出新的 ID)
如果当前的 Timestamp 比前一个 ID 的 Timestamp 大, 随机生成一个初始 sequence number (12 bits) 作为本毫秒内的第一个 sequence number
整个过程中, 只是在 Worker 启动的时候会对外部有依赖 (需要从 Zookeeper 获取 Worker 号), 之后就可以独立工作了, 做到了去中心化.
异常情况讨论:
在获取当前 Timestamp 时, 如果获取到的时间戳比前一个已生成 ID 的 Timestamp 还要小怎么办? Snowflake 的做法是继续获取当前机器的时间, 直到获取到更大的 Timestamp 才能继续工作 (在这个等待过程中, 不能分配出新的 ID)
从这个异常情况可以看出, 如果 Snowflake 所运行的那些机器时钟有大的偏差时, 整个 Snowflake 系统不能正常工作 (偏差得越多, 分配新 ID 时等待的时间越久)
从 Snowflake 的官方文档 (https://github.com/twitter/snowflake/#system-clock-dependency) 中也可以看到, 它明确要求 “You should use NTP to keep your system clock accurate”. 而且最好把 NTP 配置成不会向后调整的模式. 也就是说, NTP 纠正时间时, 不会向后回拨机器时钟.
三, Snowflake 的其他变种
Snowflake 有一些变种, 各个应用结合自己的实际场景对 Snowflake 做了一些改动. 这里主要介绍 3 种.
1. Boundary flake
http://boundary.com/blog/2012/01/12/flake-a-decentralized-k-ordered-unique-id-generator-in-erlang/
变化:
ID 长度扩展到 128 bits:
最高 64 bits 时间戳;
然后是 48 bits 的 Worker 号 (和 Mac 地址一样长);
最后是 16 bits 的 Seq Number
由于它用 48 bits 作为 Worker ID, 和 Mac 地址的长度一样, 这样启动时不需要
和 Zookeeper 通讯获取 Worker ID. 做到了完全的去中心化
基于 Erlang
它这样做的目的是用更多的 bits 实现更小的冲突概率, 这样就支持更多的 Worker 同时工作. 同时, 每毫秒能分配出更多的 ID
2. Simpleflake
http://engineering.custommade.com/simpleflake-distributed-id-generation-for-the-lazy/
Simpleflake 的思路是取消 Worker 号, 保留 41 bits 的 Timestamp, 同时把 sequence number 扩展到 22 bits;
Simpleflake 的特点:
sequence number 完全靠随机产生 (这样也导致了生成的 ID 可能出现重复)
没有 Worker 号, 也就不需要和 Zookeeper 通讯, 实现了完全去中心化
Timestamp 保持和 Snowflake 一致, 今后可以无缝升级到 Snowflake
Simpleflake 的问题就是 sequence number 完全随机生成, 会导致生成的 ID 重复的可能. 这个生成 ID 重复的概率随着每秒生成的 ID 数的增长而增长.
所以, Simpleflake 的限制就是每秒生成的 ID 不能太多 (最好小于 100次/秒, 如果大于 100次/秒的场景, Simpleflake 就不适用了, 建议切换回 Snowflake).
3. instagram 的做法
先简单介绍一下 instagram 的分布式存储方案:
先把每个 Table 划分为多个逻辑分片 (logic Shard), 逻辑分片的数量可以很大, 例如 2000 个逻辑分片
然后制定一个规则, 规定每个逻辑分片被存储到哪个数据库实例上面; 数据库实例不需要很多. 例如, 对有 2 个 PostgreSQL 实例的系统 (instagram 使用 PostgreSQL); 可以使用奇数逻辑分片存放到第一个数据库实例, 偶数逻辑分片存放到第二个数据库实例的规则
每个 Table 指定一个字段作为分片字段 (例如, 对用户表, 可以指定 uid 作为分片字段)
插入一个新的数据时, 先根据分片字段的值, 决定数据被分配到哪个逻辑分片 (logic Shard)
然后再根据 logic Shard 和 PostgreSQL 实例的对应关系, 确定这条数据应该被存放到哪台 PostgreSQL 实例上
instagram unique ID 的组成:
41 bits: Timestamp (毫秒)
13 bits: 每个 logic Shard 的代号 (最大支持 8 x 1024 个 logic Shards)
10 bits: sequence number; 每个 Shard 每毫秒最多可以生成 1024 个 ID
生成 unique ID 时, 41 bits 的 Timestamp 和 Snowflake 类似, 这里就不细说了.
主要介绍一下 13 bits 的 logic Shard 代号 和 10 bits 的 sequence number 怎么生成.
logic Shard 代号:
假设插入一条新的用户记录, 插入时, 根据 uid 来判断这条记录应该被插入到哪个 logic Shard 中.
假设当前要插入的记录会被插入到第 1341 号 logic Shard 中 (假设当前的这个 Table 一共有 2000 个 logic Shard)
新生成 ID 的 13 bits 段要填的就是 1341 这个数字
sequence number 利用 PostgreSQL 每个 Table 上的 auto-increment sequence 来生成:
如果当前表上已经有 5000 条记录, 那么这个表的下一个 auto-increment sequence 就是 5001 (直接调用 PL/PGSQL 提供的方法可以获取到)
然后把 这个 5001 对 1024 取模就得到了 10 bits 的 sequence number
instagram 这个方案的优势在于:
利用 logic Shard 号来替换 Snowflake 使用的 Worker 号, 就不需要到中心节点获取 Worker 号了. 做到了完全去中心化
另外一个附带的好处就是, 可以通过 ID 直接知道这条记录被存放在哪个 logic Shard 上
同时, 今后做数据迁移的时候, 也是按 logic Shard 为单位做数据迁移的, 所以这种做法也不会影响到今后的数据迁移
PHP QR CODE生成二维码的更多相关文章
- 使用PHP QR Code生成二维码
使用PHP QR Code生成二维码 HP QR Code是一个PHP二维码生成类库,利用它可以轻松生成二维码,官网提供了下载和多个演示demo,查看地址: http://phpqrcode.so ...
- 利用PHP QR Code生成二维码(带logo)
转自:http://www.cnblogs.com/txw1958/p/phpqrcode.html HP QR Code是一个PHP二维码生成类库,利用它可以轻松生成二维码,官网提供了下载和多个演示 ...
- 使用PHP二维码生成类库PHP QR Code生成二维码
<?php include 'phpqrcode.php'; $value = 'http://www.helloweba.com'; //二维码内容 $errorCorrectionLevel ...
- HP QR Code 实现二维码
二维码简单点说就是图片中含有数据信息,可以是url链接,也可能是其他的 首先下载该类,(http://download.csdn.net/detail/cgjcgs/9100365) 然后直接引入该类 ...
- PHP QR Code封装二维码生成教程
今天搞了一下二维码封装在tp框架上运用. 找了下草料网, api接口要收费, 现在找到了两种方法来实现用PHP来实现创建二维码. 由于二维码生成,会使用到PHP的GD库, 我们要先在PHP.ini文件 ...
- 阐述二维码的原理以及使用google api和PHP QR Code来生成二维码
一.什么是二维码:二维码 (2-dimensional bar code),是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的.在许多种类的二维条码中,常用的码 ...
- 两种PHP生成二维码的方法
PHP生成二维码,个人认为最常用的有两种,1.使用google的api生成,2.使用PHP QR Code生成,两种方法生成的二维码都是很清淅的,效果不错.下面来分别说明这两种方法如何实现. 一.PH ...
- 利用google api生成二维码名片例子
二维条码/二维码可以分为堆叠式/行排式二维条码和矩阵式二维条码.堆叠式/行排式二维条码形态上是由多行短截的一维条码堆叠而成:矩阵式二维条码以矩阵的形式组成,在矩阵相应元素位置上用“点”表示二进制“1” ...
- 使用PHP生成二维码的两种方法(带logo图像)
一.利用Google API生成二维码 Google提供了较为完善的二维码生成接口,调用API接口很简单,以下是调用代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...
随机推荐
- Shell脚本基础II
1.shell算术运算 1)加法 r=`expr 4 + 5`(注意! '4' '+' '5' 这三者之间要有空白) r=$[ 4 + 5 ] r=$(( 4 + 5 )) echo $r 2)乘法 ...
- 两个C++对象是否相等,要程序员自己下定义,通常是覆盖==操作符
我曾经好多年对Java的==和equals的区别和联系搞不清楚,后来搞清楚了,笔记在这里: http://www.cnblogs.com/findumars/p/3240761.htmlhttp:// ...
- myplan
告警集成平台 – OneAlert(空) JumpServer-开源跳板机(堡垒机)搭建(空) Suricata入侵检测.防御系统搭建(空) Snort入侵检测系统搭建(空) OpenVas开放式漏洞 ...
- URAL 1069 Prufer Code 优先队列
记录每个节点的出度,叶子节点出度为0,每删掉一个叶子,度数-1,如果一个节点的出度变成0,那么它变成新的叶子. 先把所有叶子放到优先队列中. 从左往右遍历给定序列,对于root[i],每次取出叶子中编 ...
- MAVEN “Plugin execution not covered by lifecycle configuration”
pom文件中报错提示: Plugin execution not covered by lifecycle configuration: net.alchim31.maven:yuicompresso ...
- 《OD学hadoop》第二周0703
hdfs可视化界面: http://beifeng-hadoop-01:50070/dfshealth.html#tab-overview yarn可视化界面: http://beifeng-hado ...
- dom4j API使用简介
dom4j API使用简介 功能简介 dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的.dom4j是一个非常非常优秀的Java XML API,具有性能优异.功能强大和极 ...
- Navicat数据存放位置和备份数据库路径设置
navicat的数据库存放位置在什么地方?带着这样的疑问,我们去解决问题,navicat是默认安装,mysql也是默认安装,数据库存在默认用户所在的目录下面. 安装MySQL时,请选择“Custom” ...
- IE下easyui 缓存问题
$.ajaxSetup ({ cache: false //关闭AJAX相应的缓存 }); 这一句话就足够了,很管用!
- UVa 1442 (线性扫描) Cave
对于一个水坑,水平面肯定是相等的.(废话,不然为什么叫水ping面) 因为水面不能碰到天花板,所以将水面向两边延伸要么碰到墙壁要么延伸到洞穴外面去. 设h(i)表示向左延伸不会碰到天花板的最高水平面, ...