Best way to handle concurrency issues

后端 未结 11 1201
迷失自我
迷失自我 2021-02-07 23:27

i have a LAPP (linux, apache, postgresql and php) environment, but the question is pretty the same both on Postgres or Mysql.

I have an cms app i developed, that handle

相关标签:
11条回答
  • 2021-02-07 23:28

    Donnie's answer (polling) is probably your best option - simple and works. It'll cover almost every case (its unlikely a simple PK lookup would hurt performance, even on a very popular site).

    For completeness, and if you wanted to avoid polling, you can use a push-model. There's various ways described in the Wikipedia article. If you can maintain a write-through cache (everytime you update the record, you update the cache), then you can almost completely eliminate the database load.

    Don't use a timestamp "last_updated" column, though. Edits within the same second aren't unheard of. You could get away with it if you add extra information (server that did the update, remote address, port, etc) to ensure that, if two requests came in at the same second, to the same server, you could detect the difference. If you need that precision, though, you might as well use a unique revision field (it doesn't necessarily have to be an incrementing integer, just unique within that record's lifespan).

    Someone mentioned persistent connections - this would reduce the setup cost of the polling queries (every connection consumes resources on the database and host machine, naturally). You would keep a single connection (or as few as possible) open all the time (or as long as possible) and use that (in combination with caching and memoization, if desired).

    Finally, there are SQL statements that allow you to add a condition on UPDATE or INSERT. My SQl is really rusting, but I think its something like UPDATE ... WHERE .... To match this level of protection, you would have to do your own row locking prior to sending the update (and all the error handling and cleanup that might entail). Its unlikely you'd need this; I'm just mentioning it for completness.

    Edit:

    Your solution sounds fine (cache timestamps, proxy polling requests to a another server). The only change I'd make is to update the cached timestamps on every save. This will keep the cache fresher. I'd also check the timestamp directly from the db when saving to prevent a save sneaking in due to stale cache data.

    If you use APC for caching, then a second HTTP server doesn't make sense - you'd have to run it on the same machine (APC uses shared memory). The same physical machine would be doing the work, but with the additional overhead of a second HTTP server. If you want to off load the polling requests to a second server (lighttpd, in your case), then it would be better to setup lightttpd in front of Apache on a second physical machine and use a shared caching server (memcache) so that the lighttpd server can read the cached timestamps, and Apache can update the cached timestamps. The rationale for putting lighttpd in front of Apache is, if most requests are polling requests, to avoid the heavier-weight Apache process usage.

    You probably don't need a second server at all, really. Apache should be able to handle the additional requests. If it can't, then I'd revisit your configuration (specifically the directives that control how many worker processes you run and how many requests they are allowed to handle before being killed).

    0 讨论(0)
  • 2021-02-07 23:30

    This is slightly off topic, but you can use the PEAR package (or PECL package, I forget which) xdiff to send back good user guidance when you do get a collision.

    0 讨论(0)
  • 2021-02-07 23:33

    First off only update the fields that have changed on when writing to the database, this will decrease database load.

    Second, query the timestamp of the last update, if you have a older timestamp then the current version in the database then throw the warning to the client.

    Third is to somehow push this information to the client, though some kind of persistent connection with the server, enabling a concurrent two way connection.

    0 讨论(0)
  • 2021-02-07 23:37

    Hibernate uses a version field to do that. Give every table such a field and use a trigger to increment it on every update. When storing an update, compare the current version with the version when the data was read earlier. If those don't match, throw an exception. Use transactions to make the check-and-update atomic.

    0 讨论(0)
  • 2021-02-07 23:40

    Your approach of querying the database is the best one. If you do it every 5 seconds and you have 15 concurrent users then you're looking at ~3 queries a second. It should be a very small query too, returning only one row of data. If your database can't handle 3 transactions a second then you might have to look at a better database because 3 queries/second is nothing.

    Timestamp the records in the table so you can quickly see if anything has changed without having to diff each field.

    0 讨论(0)
  • 2021-02-07 23:41

    Ahhh, i though it was easyer.

    So, lets make the point: i have a generic database (pgsql or mysql doesn't matter), that contains many generic objects.

    I have $x (actually $x = 200, but is growing, hoping will reach 1000 soon) of exact copy of this database, and for each of them up to 20 (avg 10) users for 9 hours at day.

    If one of those users is viewing a record, any record, i must advice him if someone edit the same record.

    Let's say Foo is watching the document 0001, sit up for a coffee, Bar open and edit the same document, when Foo come back he must see an 'Warning, someone else edited this document! click here to refresh tha page.'.

    That'all i need atm, probably i'll extend this situation, adding a way to see the changes and rollback, but this is not the point.

    Some of you suggested to check the 'last update' timestamp only when foo try to save the document; Can be a solution too, but i need something in real-time ( 10 sec deelay ).

    Long polling, bad way, but seem to be the only one.

    So, what i've done:

    1. Installed Lighttp on my machine (and php5 as fastcgi);
    2. Loaded apache2's proxy module (all, or 403 error will hit you);
    3. Changed the lighttpd port from 80 (that is used by apache2) to 81;
    4. Configured apache2 to proxying the request from mydomain.com/polling/* to polling.mydomain.com (served with Lighttp)
    5. Now, i have another sub http-service that i'll use both for polling and load static content (images, etc..), in order to reduce the apache2's load.
    6. Becose i dont want to nuke the database for the timestamp check, i've tryed some caches system (that can be called from php).
      1. APC: quite simple to install and manage, very lightweight and faster, this would be my first choice.. if only the cache would be sharable between two cgi process (i need to store in cache a value from apache2's php process, and read it from lighttpd's php process)
      2. Memcached: around 4-5 times slower than APC, but run as a single process that can be touched everywhere in my environment. I'll go with this one, atm. (even if is slower, the use i'll do of it is relatively simple).

    Now, i just have to try this system loading some test datas to see ho will move 'under pressure' and optimize it.

    I suppost this environment will work for other long-polling situations (chat?)

    Thanks to everyone who gave me hear!

    0 讨论(0)
提交回复
热议问题