数据同步的一些思考与改进

数据同步的一些思考与改进

背景

闲的没事,自己写了个小网站,搭建在自己国外的VPS上,VPS内存极小(512M),而且还要跑点别的(你懂的),内存更紧张巴巴. 改造之前小网站用到了时髦的Redis,Rabbmitmq,Mysql,那时候阿里云的学生主机内存富足,装这么多中间件压力不大,可到了这样的小内存VPS上,一切都变得水土不服,索性啥中间件都不要了,数据库也不要了.
没了数据库,网站的数据从哪里来?存在哪里? 文本形式持久化到本地磁盘?
国外的VPS不比国内,可能哪天说不能访问就不能访问了,VPS的磁盘存储显然不踏实.
同事给我建议了万能的Github,听过Github托管代码??,托管静态页面??,托管女装大佬??,但托管网站数据倒是第一次听说,于是我对网站架构进行了重新设计.

Plan1 数据的同步

小网站数据不多,10M左右,所有数据直接加载到内存中服务器也不会吃力,网站启动,自动从Github Clone数据,并定期把内存中的数据序列化后Push到Github.
可以看到,整个过程中,好像没有磁盘啥事了,在我的眼里,Github就是一块延时略高的磁盘(其实延时也还好,国外的Github访问速度飞快).

Plan2 同步的频率

磁盘的读取速度和内存无法比,何况远程的Github,那么如果减少数据从内存到Github的同步开销呢?显然就是减少同步的频率.
一小时同步一次,应该够了.
但如果我的网站在这一小时挂了boom??,而数据还没来得及同步,那上次一同步到网站挂掉这个时间段内的数据不就没了吗?细思极恐??!

Plan3 多多不益善

既然一小时一次不安全,那就一分钟同步一次!
其实这样也是有问题的,小网站一般都是无人问津,如果以较高的频率进行数据同步,可以说绝大多数(用互联网的所法是百分之N个9)的数据同步都是没意义的,同时还增大了数据的同步开销,没准Github还会把我的账号给封了.

Plan4 内存数据变更立即触发数据同步

在我的网站中,有统一的数据访问层,只要数据访问层中的insert,update,delete处加入数据同步事件,即可实现一旦更新立即同步.
这样是数据是安全了,可是一次访问请求往往伴随着多次数据更新,每更新一次同步一次,可能是最脑残??的做法吧.

Question

数据更改一次同步一次不合理,同步频率太低数据不安全,频率太高多数同步没有意义,到底该怎样呢?

局部性原理

在揭开我的设计方案前,我们先来过一下CPU访问存储器时所遵守的局部性原理.

在计算机存储介质这个金字塔中,越靠近金字塔顶端,空间越小,但是读取数据越快;越靠近金字塔底端,空间越大,但访问速度也越慢.
正式因为这样,所以每次自下而上的数据数据流大小逐层递增, 交换频率逐层递减,如何在时间与空间上取到平衡点是关键.
于是有了空间局部性原理和时间局部性原理,力求让计算机的数据流动更高效.

空间局部性

如果一条数据被访问,那么与它临近的数据也可能要被用到. 比如数组,你访问了索引1上的数据,那么1附近的数据当然很有可能被访问,所以这个时候干脆把1附近的数据也往上加载一个层级.

时间局部性

如果一条数据项正在被访问,那么在近期它很可能还会被再次访问,所以这个时候干脆就把它留在当前层级,先不急着回收掉.

而网站的数据的更新也是具有时间局部性的,像我这样并冷门的网站,基本没人访问,但是一旦访问了,立即就要进行点击量的更新,站点响应速度的记录,没准又会有评论留言,然后要通知管理员进行留言审核.这大概就是不鸣则已,一鸣惊人,一次访问短期内往往立即触发一连串的数据更新,我认为这也是一种时间局部性.

所以,在数据同步上,我设计了如下方案.

  • 另起一个线程作为定时任务,主要负责定时数据同步
  • 正常情况下,每小时与Github进行数据同步.
  • 一旦网站数据被更新,检查剩余同步时间是否大于30秒.
    ** 如果大于三十秒,强行把计时器剩余时间设置为30秒.
    ** 如果小于三十秒,不做操作.
  • 计时器时间走完,立即同步数据到Github.

定时沙漏?

原本文章说到这里就可以结束了,但程序员注定爱代码爱过文字,又恰好我天生爱造轮子,我从令牌桶得到灵感设计了一个乞丐版沙漏计时器,可以用于任何定时任务的执行,班门弄斧,欢迎提出改进意见.

Show time

public class BlogsTimer
{
    private static Stack<int> _upFunnel;  //沙漏上部分
    private static Stack<int> _downFunnel;  //沙漏下部分
    private static readonly List<Action> TimerEvents;  //定时执行的事件
    private static bool _timerSwitch;  //沙漏开关
    private static readonly int Speed;  //每秒消费令牌数量
    private static Thread _timerThread;
    private static readonly object TimerLock;
    static BlogsTimer()
    {
        _upFunnel = new Stack<int>();
        _downFunnel = new Stack<int>();
        Speed = 1 * 1000;
        TimerEvents = new List<Action>();
        TimerLock = new object();
    }
    //计时器开始
    public static void Start(TimeSpan timeSpan)
    {
        lock (TimerLock)
        {
            _upFunnel.Clear();
            _downFunnel.Clear();
            for (var i = 0; i < timeSpan.TotalSeconds; i++)
            {
                _upFunnel.Push(i);
            }
        }
        _timerSwitch = true;
        _timerThread = new Thread(Consume); //起一个线程消费桶里的令牌
        _timerThread.Start();
        LunchEvents(); // 触发事件
    }
    public static void Stop()
    {
        _timerSwitch = false;
    }

    //给沙漏注册定时执行事件
    public static void Register(Action timeEvent)
    {
        TimerEvents.Add(timeEvent);
        timeEvent.Invoke();
    }

    //把沙漏加速到指定的时间
    public static void AccelerateTo(TimeSpan timeSpan)
    {
        var accelerateSeconds = timeSpan.TotalSeconds;
        lock (TimerLock)
        {
            if (_upFunnel.Count < accelerateSeconds) //当前沙漏中剩余令牌小于设置中秒数,则返回不加速
                return;
            while (_upFunnel.Count > accelerateSeconds && _upFunnel.Count > 1)  //令牌数大于秒数,则释放出多余令牌
            {
                _downFunnel.Push(_upFunnel.Pop());
            }
        }
    }
    private static void LunchEvents()
    {
        TimerEvents.ForEach(a => a.Invoke());
    }
    private static void Consume()
    {
        while (_timerSwitch)
        {
            lock (TimerLock)
            {
                if (_upFunnel.TryPop(out var item))
                {
                    _downFunnel.Push(item);
                }
                else
                {
                    LunchEvents();
                    var tempStack = _downFunnel;  //旋转沙漏
                    _downFunnel = _upFunnel;
                    _upFunnel = tempStack;
                }
            }
            Thread.Sleep(Speed);
        }
    }
}

源码地址: https://github.com/liuzhenyulive/iBlogs/blob/master/Src/iBlogs.Site.Core/Common/iBlogsTimer.cs
演示地址: https://www.iblogs.site

原文地址:https://www.cnblogs.com/CoderAyu/p/11839520.html

时间: 2024-10-17 20:59:38

数据同步的一些思考与改进的相关文章

数据同步这点事

最近一段时间,在做数据ETL相关的事,结合实践以及自己的思考,记录下来,以做参考. 概述 一般来说,数据团队自己是很少生产数据的,一般都是对业务线的数据进行分析加工,从而让数据产生价值.一方面,业务线的数据会存到关系数据(如mysql),磁盘(日志)等存储介质:另一方面,基于大数据的分析一般会将数据存储到hdfs,hbase,es.因此,不可避免地我们需要在这些不同的存储介质间同步数据.从同步时效性来说,可以分为离线同步和实时同步.离线同步,相当于某个时候对源数据做一个快照.而实时同步,一般是通

使用Sqlserver事务发布实现数据同步(zhuanqian)

事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案,所以有机会再次研究一下它以及快照等,发现还是有很多不错的功能和改进的.这里以sqlserver2008的事务发布功能为例,对发布订阅的方式简要介绍一下操作流程,一方面做个总结备份,一方面与大家进行一下分享和交流.费话就不多说了,进入一下今天的正题:) 这里要说明一下环境:首先我在本地局域网内有两台安装有sqlserver2008的机器(注意:已发布的快照版本无法向老版本数据库兼容,意味着2008下创建的事务或快照发布,无法被

Sqlserver事务发布实现数据同步

事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案,所以有机会再次研究一下它以及快照等,发现还是有很多不错的功能和改进的.这 里以sqlserver2008的事务发布功能为例,对发布订阅的方式简要介绍一下操作流程,一方面做个总结备份,一方面与大家进行一下分享和交流.费话 就不多说了,进入一下今天的正题:) 这里要说明一下环境:首先我在本地局域网内有两台安装有sqlserver2008的机器(注意:已发布的快照版本无法向老版本数据库兼容,意味着 2008下创建的事务或快照发布,

分布式数据库数据从属与client与server的数据同步

老实说,眼下市面上很多产品,的确是不成熟的产品. 用过一些,给人蛋痛的感觉. 导言 分布还是集总 今天我们来探讨一个非常重要的问题. 每一个程序猿都有其思想,我的思想之中的一个,就是分布式. 分布式,面对的一个问题,就数据的同步. 比方说.我们人类是分布式的,我们每一个细胞都在无时无刻与其他细脑交换数据. 而现实世界.我们的设计是什么样子?一般都是集总式. 首先来说,这样的方式,与现实世界并不一致.所以.带来的最严重的一个影响就是效率的问题. 自己这些年,一直在无线通信领域. 无线通信.有两个重

使用Sqlserver事务发布实现数据同步

事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案,所以有机会再次研究一下它以及快照等,发现还是有很多不错的功能和改进的.这里以sqlserver2008的事务发布功能为例,对发布订阅的方式简要介绍一下操作流程,一方面做个总结备份,一方面与大家进行一下分享和交流.费话就不多说了,进入一下今天的正题:) 这里要说明一下环境:首先我在本地局域网内有两台安装有sqlserver2008的机器(注意:已发布的快照版本无法向老版本数据库兼容,意味着2008下创建的事务或快照发布,无法被

Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解

   我们知道在多线程编程中,我们很大的一部分内容是为了解决线程间的资源同步问题和线程间共同协作解决问题.线程间的同步,通俗我们理解为僧多粥少,在粥有限情况下,我们怎么去防止大家有秩序的喝到粥,不至于哄抢都没得喝.线程讲协作,我们可以理解为我们在医院看病的时候,我们要先挂号,才能看病.现在医院有很多病人排队,怎么协调病人都有秩序的先挂号,后看病.本篇文章的重点不在此,也不是在此一下子能分析完,我们先从Java JVM的角度来理解多线程的一些方面. 我们知道多线程间的数据同步,我们是通过加锁的操作

C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 大型软件系统客户端数据同步的问题解决

作为一个完整的整体信息化解决方案需要有足够强大的各种功能,这些功能相对独立,又互相依存.当有需要这样的功能时可以随时拿出来用,适当修改一下就可以满足要求.只有这样才能快速开发各种信息化系统,才能满足各种客户的需求. 同步数据需要解决的问题主要有: 01:全国性大型集团公司的信息化改造项目会涉及到非常多的分公司网点的客户端需要同步数据的各种需求,这个已经超越了人工能处理好的极限.02:网点非常多时,往往由于业务的特殊性会有能离线作业的需要,网络故障.网络带宽不理想时都可以进行离线处理的工作流程需要

分布式数据库数据从属与客户端与服务器的数据同步

老实说,目前市面上许多产品,的确是不成熟的产品. 用过一些,给人蛋痛的感觉. 导言 分布还是集总 今天我们来探讨一个很重要的问题. 每个程序员都有其思想,我的思想之一,就是分布式. 分布式,面对的一个问题,就数据的同步. 比如说,我们人类是分布式的,我们每个细胞都在无时无刻与其它细脑交换数据. 而现实世界,我们的设计是什么样子?一般都是集总式. 首先来说,这种方式,与现实世界并不一致.所以,带来的最严重的一个影响就是效率的问题. 自己这些年,一直在无线通信领域. 无线通信,有两个重要的特点: 1

C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 能支撑10万以上客户端的数据同步下载问题

庞大的业务系统,特别是需要有离线作业操作支持的核心业务系统,需要有强大的基础数据同步功能,基础数据有在增加.有在变动.有在失效,同时有大量的客户端全天侯的在连接服务器.不间断的在处理核心数据. 经过2年的不断完善改进.又有保证性能.又有能保证性能.又支持自动升级的接近完美的客户端诞生了.在这个基础上开发任何业务模块都会方便很多,客户端支持手动同步基础技术.登录系统时后台自动同步数据等功能. 其实很多时候,难点不在于技术有多少深奥.在于稳定可靠.放心用,经得起大量客户端的参考考验. 把每一个点点滴