欢迎来到Heck's Blog,专业承接拿站、企业建站、仿站、网上商城架构、门户网站搭建、空间域名注册、软件定制等项目。关注网络安全,因为专注,所以专业,懂得放弃,才能收获。有事请发邮件至i@heckjj.com,请记住本站网址:http://www.heckjj.com,多谢。
1月19
mysql分区后每个分区成了独立的文件,虽然从逻辑上还是一张表其实已经分成了多张独立的表,从“information_schema.INNODB_SYS_TABLES”系统表可以看到每个分区都存在独立的TABLE_ID,由于Innodb数据和索引都是保存在".ibd"文件当中(从INNODB_SYS_INDEXES系统表中也可以得到每个索引都是对应各自的分区(primary key和unique也不例外)),所以分区表的索引也是随着各个分区单独存储。

在INNODB_SYS_INDEXES系统表中type代表索引的类型;

0:一般的索引,

1:(GEN_CLUST_INDEX)不存在主键索引的表,会自动生成一个6个字节的标示值,

2:unique索引,

3:primary索引;

所以当我们在分区表中创建索引时其实也是在每个分区中创建索引,每个分区维护各自的索引(其实也就是local index);对于一般的索引(非主键或者唯一)没什么问题由于索引树中只保留了索引key和主键key(如果存在主键则是主键的key否则就是系统自动生成的6个的key)不受分区的影响;但是如果表中存在主键就不一样了,虽然在每个分区文件中都存在主键索引但是主键索引需要保证全局的唯一性就是所有分区中的主键的值都必须唯一(唯一键也是一样的道理),所以在创建分区时如果表中存在主键或者唯一键那么分区列必须包含主键或者唯一键的部分或者全部列(全部列还好理解,部分列也可以个人猜测是为了各个分区和主键建立关系),由于需要保证全局性又要保证插入数据更新数据到具体的分区所以就需要将分区和主键建立关系,由于通过一般的索引进行查找其它非索引字段需要通过主键如果主键不能保证全局唯一性的话那么就需要去每个分区查找了,这样性能可想而知。

To enforce the uniqueness we only allow mapping of each unique/primary key value to one partition.If we removed this limitation it would mean that for every insert/update we need to check in every partition to verify that it is unique. Also PK-only lookups would need to look into every partition.

索引方式:
性能依次降低
1.主键分区

主键分区即字段是主键同时也是分区字段,性能最好

2. 部分主键+分区索引

使用组合主键里面的部分字段作为分区字段,同时将分区字段建索引(见下面详细说明)

3.分区索引

没有主键,只有分区字段且分区字段建索引

4.分区+分区字段没有索引

只建了分区,但是分区字段没有建索引

总结

因为每一个表都需要有主键这样可以减少很多锁的问题,由于上面讲过主键需要解决全局唯一性并且在插入和更新时可以不需要去扫描全部分区,造成主键和分区列必须存在关系;所以最好的分区效果是使用主键作为分区字段其次是使用部分主键作为分区字段且创建分区字段的索引,其它分区方式都建议不采取。


MYSQL的分区字段,必须包含在主键字段内
在对表进行分区时,如果分区字段没有包含在主键字段内,如表A的主键为ID,分区字段为createtime ,按时间范围分区,代码如下:

CREATE TABLE T1 (
     id int(8) NOT NULL AUTO_INCREMENT,
     createtime datetime NOT NULL,
      PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
PARTITION BY RANGE(TO_DAYS (createtime))
(
PARTITION p0 VALUES LESS THAN (TO_DAYS('2010-04-15')),
PARTITION p1 VALUES LESS THAN (TO_DAYS('2010-05-01')),
PARTITION p2 VALUES LESS THAN (TO_DAYS('2010-05-15')),
PARTITION p3 VALUES LESS THAN (TO_DAYS('2010-05-31')),
PARTITION p4 VALUES LESS THAN (TO_DAYS('2010-06-15')),
PARTITION p19 VALUES LESS ThAN  MAXVALUE);
复制代码
错误提示:#1503

MySQL主键的限制,每一个分区表中的公式中的列,必须在主键/unique key 中包括,在MYSQL的官方文档里是这么说明的
18.5.1. Partitioning Keys, Primary Keys, and Unique Keys
This section discusses the relationship of partitioning keys with primary keys and unique keys. The rule governing this relationship can be expressed as follows: All columns used in the partitioning expression for a partitioned table must be part of every unique key that the table may have.

In other words,every unique key on the table must use every columnin the table's partitioning expression. (This also includes the table's primary key, since it is by definition a unique key. This particular case is discussed later in this section.) For example, each of the following table creation statements is invalid:
分区字段必须包含在主键字段内,至于为什么MYSQL会这样考虑,我觉得是这样的:为了确保主键的效率。否则同一主键区的东西一个在A分区,一个在B分区,显然会比较麻烦。

下面讨论解决办法,毕竟在一张表里,日期做主键的还是不常见。
方法1:
顺应MYSQL的要求,就把分区字段加入到主键中,组成复合主键
CREATE TABLE T1 (
     id int(8) NOT NULL AUTO_INCREMENT,
     createtime datetime NOT NULL,
      PRIMARY KEY (id,createtime)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
PARTITION BY RANGE(TO_DAYS (createtime))
(
PARTITION p0 VALUES LESS THAN (TO_DAYS('2010-04-15')),
PARTITION p1 VALUES LESS THAN (TO_DAYS('2010-05-01')),
PARTITION p2 VALUES LESS THAN (TO_DAYS('2010-05-15')),
PARTITION p3 VALUES LESS THAN (TO_DAYS('2010-05-31')),
PARTITION p4 VALUES LESS THAN (TO_DAYS('2010-06-15')),
PARTITION p19 VALUES LESS ThAN  MAXVALUE);
测试通过,分区成功。
1月18
大家都知道删除分区的语句如下:
ALTER TABLE '表名' DROP PARTITION '分区名' ; 但是这样的话只能一个个分区去删除,而且删除分区同时会把数据也给删除掉了,那么mysql删除表的所有分区如何操作呢?
只要使用ALTER TABLE 表名 REMOVE PARTITIONING 就可以删除所有分区,但数据不会被删除。

查询指定表的分区信息以及每个分区的行数
SELECT PARTITION_NAME,TABLE_ROWS
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME = '表名';

SELECT
  PARTITION_NAME,PARTITION_DESCRIPTION,
FROM_UNIXTIME(PARTITION_DESCRIPTION,'%Y-%m-%d') EXPIRYDATE,TABLE_ROWS
FROM
INFORMATION_SCHEMA.PARTITIONS where TABLE_NAME ='表名';
1月18
今天主要介绍下面这几个错误:

ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning

ERROR 1503 (HY000): A UNIQUE INDEX must include all columns in the table's partitioning function

主键必须包括表的分区函数中的所有列,一个惟一的索引必须包括表的分区函数中的所有列,是不是不太好理解意思

其实就是这么个意思:表上的每一个唯一性索引必须用于分区表的表达式上(其中包括主键索引)

下面我来举几个例子:


CREATE TABLE t1 (  
     id INT NOT NULL,  
     aid DATE NOT NULL,  
     bid INT NOT NULL,  
     PRIMARY KEY (id)  
)  
PARTITION BY KEY(bid)  
PARTITIONS 10;
[Err] 1503 - A PRIMARY KEY must include all columns in the table's partitioning function


CREATE TABLE t1 (  
     id INT NOT NULL,  
     aid DATE NOT NULL,  
     bid INT NOT NULL,    
     UNIQUE KEY (id)  
)  
PARTITION BY KEY(bid)  
PARTITIONS 10;
[Err] 1503 - A PRIMARY KEY must include all columns in the table's partitioning function


CREATE TABLE t1 (  
     id INT NOT NULL,  
     aid DATE NOT NULL,  
     bid INT NOT NULL,  
     PRIMARY KEY (id,bid)  
)  
PARTITION BY KEY(bid)  
PARTITIONS 10;
Query OK, 0 rows affected (0.535 sec)

1月13
客户要求在手机端页面能直接点击电话可以打电话的功能,测试了下在H5页面调用手机打电话和发短信的功能按以下步骤:
<head>里面加上:

<meta name="format-detection" content="telephone=yes"/>
  

需要拨打电话的地方:
<a href="tel:400-0000-688">400-0000-688</a>
  

发短信:
<a href="sms:18688888888">发短信</a>
  
点击 页面上的电话号码 ,页面会调用手机的电话接口。
1月6
使用场景,多个接口的某个或多个参数都是从登录态中获取的,如下的capCode,这里只设计修改入参。
@Order(1)
@Component
@Aspect
@Slf4j
public class CapCodeAspect {

    @Autowired
    private TokenService tokenService;

    public CapCodeAspect() {
    }

    @Pointcut("@annotation(com.xx.oo.annotation.CapCode)")
    private void parameterPointCut() {
    }

    @Around("parameterPointCut()")
    public Object getCapCode(ProceedingJoinPoint joinPoint) {
        try {
            Object[] o = joinPoint.getArgs();
            if (null != o) {
                for (Object ob : o) {
                    changObjectValue(ob, "capCode", gatCapCode());
                }
            }
            return joinPoint.proceed(o);
        } catch (Throwable e) {
            log.error("目标方法执行异常,目标类:" + joinPoint.getTarget() + "方法:" + joinPoint.getSignature().getName(), e);
            throw new RuntimeException("系统繁忙,请稍后再试!");
        }
    }


    /***
     * 修改入参具体字段值
     */
    private Object changObjectValue(Object obj, String filedName, Object afterValue) throws Exception {
        Class&lt;?&gt; resultClz = obj.getClass();
        //获取父类的字段,因为capCode放在在基类里面,这里就直接获取父类的Class了,
//参数只在当前类的可以添加当前类的逻辑
        Field[] fieldInfo = resultClz.getSuperclass().getDeclaredFields();
        for (Field field : fieldInfo) {
            if (filedName.equals(field.getName())) {
                field.setAccessible(true); //成员变量为private,故必须进行此操
                Object fieldValue = field.get(obj);
                field.set(obj, afterValue);
                break;
            }
        }
        return obj;
    }


    private String gatCapCode() {
        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
        SysUser user = loginUser.getUser();
        String capCode = user.getProviderId();
        log.info("资金编码:{}", capCode);
        return capCode;
    }


}
1月4
工作中发现一个问题。当我用HttpURLConnection去连接读取一个获取每年的节假日接口信息时,老是会发生这个403错误,通过浏览器访问时显示状态码为304,但量使用代码HttpURLConnection去请求会报403的错误,这个引起了IOException,导致获取的直一直为空,但是我使用浏览器请求这个接口时时就没问题。 google后知道了答案。原来如果用java代码HttpURLConnection去连的话 http header 中的User-Agent就为空,解决方法就是在连接之前先设置这个属性。

节假日获取接口:http://timor.tech/api/holiday/year/2022
12月31
公司有个项目运维管理平台中用户管理模块添加用户时填写用户邮箱反馈说不能带点,如heck.jiang@heckjj.com这种,这明显不合理呀,而且这种情况也很常见。如下图:
点击在新窗口中浏览此图片
于是乎拉下前端代码发现,校验邮箱的正则是这样的“{ required: true, message: '请输入正确的邮箱', pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, trigger: 'blur' }”,明显达到期望,自己重新写了一个如下:
^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
用校验工具校验了一下,成功匹配到了heck.jiang@heckjj.com这个邮箱。


12月29
一、所有库在同一节点上
 当业务垂直拆分出很多业务库的时候,如果都部署在同一个源上(同一个机器节点上),那么这种情况是最好办的,直接库名+表名 join就可以。

二、不在一个节点

当不同的库来自不同的节点,那么就没办法直接join查询了,有以下方式和思路:
1.全局表
  所谓全局表,就是有可能系统中所有模块都可能会依赖到的一些表。比较类似我们理解的“数据字典”。为了避免跨库join查询,我们可以将这类表在其他每个数据库中均保存一份。同时,这类数据通常也很少发生修改(甚至几乎不会),
  所以也不用太担心“一致性”问 题。

2.字段冗余
  这是一种典型的反范式设计,在互联网行业中比较常见,通常是为了性能来避免join查询。字段冗余能带来便利,是一种“空间换时间”的体现。但其适用场景也比较有限,比较适合依赖字段较少的情况。

  最复杂的还是数据一致性问题,这点很难保证,可以借助数据库中的触发器或者在业务代码层面去保证。当然,也需要结合实际业务场景来看一致性的要求。


3.数据同步
  定时A库中的tab_a表和B库中tbl_b有关联,可以定时将指定的表做同步。当然,同步本来会对数据库带来一定的影响,需要性能影响和数据时效性中取得一个平衡。这样来避免复杂的跨库查询。笔者曾经在项目中是通过ETL工具来实施的。


4.第三方插件
    1.cobar
    2.MyCAT
    3.shardding-jdbc
12月29
最近在做项目时有一个这样需求,上传文件需要显示预览,当我了解之后,我以为和普通的下载一样呢,结果后台返回的是文件流格式,据说这是因为后台是分布式部署,不能持久化存储,没办法,只能前端来处理这个文件流了,用ajax请求图片资源,服务器以文件流的形式返回。

1、返回类型需要设置为“blob”,所以需要用原生ajax,不能使用jq(原因:jquery将返回的数据转换为了string,不支持blob类型)(当然,你也可以引入组件拓展jq的能力,我知道的有一个:jquery-ajax-blob-arraybuffer.js);

2、使用FileReader将文件流转换成base64格式;

3.1、然后通过模拟点击a标签下载该文件;

3.2、或者可以设置在img元素的src属性上,在当前页面显示出来。

var xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);    // 也可以使用POST方式,根据接口
  xhr.responseType = "blob";  // 返回类型blob
  // 定义请求完成的处理函数,请求前也可以增加加载框/禁用下载按钮逻辑
  xhr.onload = function () {
    // 请求完成
    if (this.status === 200) {
      // 返回200
      var blob = this.response;
      var reader = new FileReader();
      reader.readAsDataURL(blob);  // 转换为base64,可以直接放入a表情href
      reader.onload = function (e) {
        // 转换完成,创建一个a标签用于下载
        var a = document.createElement('a');
        a.download = 'data.xlsx';
        a.href = e.target.result;
        $("body").append(a);  // 修复firefox中无法触发click
        a.click();
        $(a).remove();
      }
    }
  };
  // 发送ajax请求
  xhr.send()


xhr.open('POST', CONTACT_ACTION_FILES_DOWNLOAD, true);
            xhr.setRequestHeader("Accept", "application/json;q=0.5");
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.responseType = "blob";  // 返回类型blob
            // 定义请求完成的处理函数,请求前也可以增加加载框/禁用下载按钮逻辑
            xhr.onload = function () {
                // 请求完成
                if (this.status === 200) {
                    var blob = this.response;
                    var reader = new FileReader();
                    reader.readAsDataURL(blob);  // 转换为base64,可以直接放入a表情href
                    reader.onload = function () {
                        $("#imgTest").attr("src", reader.result);
                    }
                }
            };
// 发送ajax请求
xhr.send("filePath=" + filePath);

下载文件
export const downloadStream = (method = 'GET', url, downloadName) => {
  const ctoken = getCookie('AIB_TOKEN')
  if (ctoken) {
    let xmlResquest = new XMLHttpRequest();
    xmlResquest.open(method, url, true);
    xmlResquest.setRequestHeader("Content-type", "application/json;charset=UTF-8");
    //这里需要在请求头添加该参数,已防止CSRF
    xmlResquest.setRequestHeader("csrfToken", hashStr(ctoken));
    xmlResquest.responseType = "blob";
    xmlResquest.onload = function (oEvent) {

      let content = xmlResquest.response;
      let elink = document.createElement('a');
      elink.download = downloadName;
      elink.style.display = 'none';
      let blob = new Blob([content]);
      elink.href = URL.createObjectURL(blob);

      document.body.appendChild(elink);
      elink.click();
      document.body.removeChild(elink);
    };
    xmlResquest.send();
  }

}
12月25
一. 错误信息
先上阿里云上的报警信息。有个最大的问题是:top命令查看自己服务器CPU运行情况,会发现kdevtmpfsi的进程,CPU使用率为100%,第一次删除干净了kdevtmpfsi程序,没曾想几分钟以后,就出现了第二个警告。使用netstat -antp命令查看端口使用情况,又出现了kdevtmpfsi如图所示

netstat -antp

点击在新窗口中浏览此图片

二.解决方法
一般出现kdevtmpfsi病毒都会伴有定时任务,就会出现我上面说的处理一次后,又会继续出现,反反复复处理不干净。

1.首先停掉kdevtmpfsi的程序
ps aux
找到kdevtmpfsi的进程

删除掉与kdevtmpfsi相关的进程
点击在新窗口中浏览此图片
kill -9 20267
kill -9 20367

2.删除Linux下的异常定时任务
crontab -l 查看定时任务
crontab -r 表示删除用户的定时任务,当执行此命令后,所有用户下面的定时任务会被删除

如下图所示点击在新窗口中浏览此图片

3.结束kdevtmpfsi进程及端口占用
netstat -antp

找到kdevtmpfsi端口 我这里是28244 图中可以看到。不要直接杀掉,因为有守护线程还会重启。

ps -aux | grep kinsing
ps -aux | grep kdevtmpfsi



kill -9 28244
kill -9 28829

4.删除掉kdevtmpfsi的相关文件
cd  /tmp
ls
rm -rf kdevtmpfsi
rm -rf /var/tmp/kinsing  
最后自己可以再检查一下是否还有kdevtmpfsi的相关文件,有的话就继续删除
find / -name kdevtmpfsi
find / -name kinsing

三.怎么预防处理这个病毒
最根本的原因可能是自己的redis 6379配置不当导致的。大家可以参考阿里云的Redis服务安全加固
当然也可能是弱密码,被强行扫描了,建议定期更换系统密码。

也可以使用下面工具清除
#! /bin/sh
step=1
for (( i = 0; i < 60; i = (i+step) )); do
                date >> /data/shell/clear_date_log.txt
                KID=$(ps -ef |grep kdevtmpfsi |grep -w 'kdevtmpfsi'|grep -v 'grep'|awk '{print $2}')
                if [ $KID];then #

                                                              echo "[info]kdevtmpfsiIDΪ:$KID" >> /data/shell/clear_date_log.txt
                kill -9 $KID
                rm -f /tmp/kdevtmpfsi
    fi
                sleep $step
done

exit 0

复制上面代码另存为kill_kdevtmpfsi.sh
分页: 1/58 第一页 1 2 3 4 5 6 7 8 9 10 下页 最后页 [ 显示模式: 摘要 | 列表 ]